This is an R Markdown Notebook for analysis using data on the DC Bus System (WMATA Metrobus). The data were obtained here:

https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/

Control + Alt + Shift + m = rename in scope

Load the packages to be used.

package ‘maptools’ was built under R version 3.2.5

Get the Bus data.

First let’s check the working directory.

getwd()
[1] "/Users/mdturse/Desktop/Analytics/DCMetroBus"

Then, actually get the data.

The working directory was changed to /Users/mdturse/Desktop/Analytics/DCMetroBus/Bus AVL Oct 2016 inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the the working directory for notebook chunks.cannot open file '20161003MetrobusAVL.txt': No such file or directoryError in file(file, "rt") : cannot open the connection

Put the daily data together.


AllDays <- bind_rows(list(Oct03Raw, Oct04Raw, Oct05Raw, Oct06Raw, Oct07Raw),
                     .id = c("group")
                    )
# dim(AllDays)
str(AllDays)

Deleting old data frames.


for (i in 3:7){
  rm(list = ls(pattern = paste0("Oct0", i, "Raw")
              )
    )
  
  message("Deleting Oct0", i, "Raw")
  }

Updating variable types.

Then, sorting the data and adding a RowNumber (to be used for identifying rows later in the analyses.)


rm(i)


AllDays$group <- factor(AllDays$group)
AllDays$Route_Direction <- factor(AllDays$Route_Direction)
AllDays$Event_Time <- as.POSIXct(AllDays$Event_Time, format = "%m-%d-%y %I:%M:%S %p")
AllDays$Departure_Time <- as.POSIXct(AllDays$Departure_Time, format = "%m-%d-%y %I:%M:%S %p")

str(AllDays)


AllDays_Sorted <- arrange(AllDays,
                          Bus_ID,
                          Event_Time
                         ) %>% 
  mutate(RowNum_OG = row_number() # this is useful in identify the row later on
        )

rm(AllDays)
str(AllDays_Sorted)

# View(head(AllDays_Sorted, 100))

Inspecting the values of Stop_ID, and finding that it can take the values “” (blank) and “NULL”.


View(group_by(AllDays_Sorted,
              Stop_ID
             ) %>% 
       summarise(
         Cnt = n()
         ) %>% 
       arrange(Stop_ID)
    )

View(filter(AllDays_Sorted,
            is.na(Stop_ID) |
              Stop_ID == "" |
              Stop_ID == "NULL"
           ) %>% 
       arrange(Stop_Desc)
    )

Creating a table of distinct Stop_Desc values when Stop_ID is “” (blank) or “NULL”.


StopID_New <- filter(AllDays_Sorted,
                     is.na(Stop_ID) |
                       Stop_ID == "" |
                       Stop_ID == "NULL"
                    ) %>% 
  select(Stop_ID, Stop_Desc) %>% 
  distinct() %>% 
  arrange(Stop_ID, Stop_Desc) %>% 
  mutate(StopID_New = 1:nrow(.)
        )

View(StopID_New)

Creating a full updated table by filling in StopID_New for when Stop_ID is “” (blank) or NULL.


AllDays_StopIDNew <- left_join(AllDays_Sorted,
                               select(StopID_New,
                                      Stop_Desc,
                                      StopID_New
                                     ),
                               by = c("Stop_Desc" = "Stop_Desc")
                              ) %>% 
  mutate(StopID_Clean = ifelse(is.na(StopID_New),
                               Stop_ID,
                               StopID_New
                              ),
         StopID_Indicator = factor(ifelse(is.na(StopID_New),
                                          "ID_OK",
                                          "ID_Bad"
                                         )
                                  )
        )

rm(StopID_New)
rm(AllDays_Sorted)
str(AllDays_StopIDNew)

# View(tail(AllDays_StopIDNew, 500))
# View(filter(AllDays_StopIDNew,
#             Stop_Desc == "METROWAY ANNNOUCEMNT CORR"
#            )
#     )

Lat Long stats for pulling in Zip codes later.


LL_Stats <- group_by(AllDays_StopIDNew,
                     StopID_Clean
                    ) %>% 
  summarise(Lat_Mean = mean(Latitude, na.rm = TRUE),
            Lat_Med = median(Latitude, na.rm = TRUE),
            Lng_Mean = mean(Longitude, na.rm = TRUE),
            Lng_Med = median(Longitude, na.rm = TRUE)
           ) %>% 
  mutate(Lat_MeaLessMed = Lat_Mean - Lat_Med,
         Lng_MeaLessMed = Lng_Mean - Lng_Med,
         RowNum = row_number()
        )

str(LL_Stats)
summary(LL_Stats)

View(head(arrange(LL_Stats,
                  Lat_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lat_MeaLessMed)
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  Lng_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lng_MeaLessMed)
                 ),
          500
         )
    )

Pulling in Zip Code data from api.geonames.org.


# URL EXAMPLE:
# http://api.geonames.org/findNearbyPostalCodesJSON?lat=38.89560&lng=-76.94873&radius=0&username=supermdat

url_1 <- "http://api.geonames.org/findNearbyPostalCodesJSON?lat="
url_2 <- "&lng="
url_3 <- "&radius=0&username="
username <- "supermdat"


# need to group in bunches as http://api.geonames.org limits pulls to 2000 per hour


##### Store everything in multiple lists
pages1 <- list()


system.time(
#   for(j in 0:5){
#   for(k in ((max_row_per_hr*j) + 1):(max_row_per_hr*(j+1)
#                                     )
#      ){
#     
#   }
# }
for(i in 1:1000){
  lat <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lat_Med)
  
  lng <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lng_Med)
  
  APIData1 <- fromJSON(paste0(url_1,
                              lat,
                              url_2,
                              lng,
                              url_3,
                              username
                             ),
                       flatten = TRUE
                      )
  
  message("Retrieving Zip Code ", i)
  
  pages1[[i]] <- APIData1$postalCodes
  
  # Sys.sleep(3900)
}
)

# class(APIData1)
# class(APIData2$postalCodes)
# str(APIData1)
# head(APIData1)
# class(pages1)
# str(pages1)
# nrow(page1)
# ncol(page1)
# pages1[[1199]]
# pages1[[2051]]


##### Combine the lists into one page
Zips1 <- rbind.pages(pages1[sapply(pages1, length) > 0])


##### Combine all pages
Zips_All <- bind_rows(Zips0,
                      Zips1,
                      Zips2,
                      Zips3,
                      Zips4,
                      Zips5,
                      Zips6,
                      Zips7,
                      Zips8,
                      Zips9,
                      Zips10,
                      # Zips1_a,
                      .id = "id"
                     ) %>% 
  mutate(UniqueLatLng = paste(lat, lng, sep = "__")
        )

# str(Zips_All)
# View(head(Zips_All))


# str(LL_Stats)
LL_Stats_UnqLatLng <- mutate(LL_Stats,
                             UniqueLatLng = paste(Lat_Med, Lng_Med, sep = "__")
                            )

# str(LL_Stats_UnqLatLng)
# View(head(LL_Stats_UnqLatLng))


LL_StatsZips <- left_join(LL_Stats_UnqLatLng,
                          Zips_All,
                          by = c("UniqueLatLng" = "UniqueLatLng")
                         )

str(LL_StatsZips)
# View(head(LL_StatsZips))

# Not sure whey these couldn't be found (why they're NA)
View(filter(LL_StatsZips,
            is.na(postalCode)
           )
    )

Join to create one dataset that also includes Zip variables.


rm(url_1, url_2, url_3, username, pages0, pages1, pages2, pages3, pages4, pages5, pages6, pages7, pages8, pages9, pages10, i, lat, lng, APIData0, APIData1, APIData2, APIData3, APIData4, APIData5, APIData6, APIData7, APIData8, APIData9, APIData10, LL_Stats, LL_Stats_UnqLatLng)


AllDays_Zips <- left_join(AllDays_StopIDNew,
                          LL_StatsZips,
                          by = c("StopID_Clean" = "StopID_Clean")
                         ) %>% 
  rename(Stop_State = adminCode1,
         Stop_County = adminName2,
         Stop_City = placeName,
         Stop_Zip = postalCode
         )

rm(AllDays_StopIDNew, LL_StatsZips)
str(AllDays_Zips)

Updating variable types.


AllDays_Zips$Stop_State <- factor(AllDays_Zips$Stop_State)
AllDays_Zips$Stop_County <- factor(AllDays_Zips$Stop_County)
AllDays_Zips$Stop_Zip <- factor(AllDays_Zips$Stop_Zip)
AllDays_Zips$Stop_City <- factor(AllDays_Zips$Stop_City)

AllDays_Zips$distance <- as.numeric(AllDays_Zips$distance)
AllDays_Zips$countryCode <- factor(AllDays_Zips$countryCode)
AllDays_Zips$adminName1 <- factor(AllDays_Zips$adminName1)

str(AllDays_Zips)

Feature engineering.

Inspecting incidences of consecutive Stop_IDs. This is done because investigation showed that many conseutive events occurr at the same Stop_ID, but with various Dwell_Times, Odometer_Distances, etc. All of which affect calculations and analyses.

Create data on the runs (consecutive Stop_IDs).


StopID_Runs <- rle(AllDays_Zips$StopID_Clean)

StopID_Runs$ends <- cumsum(StopID_Runs$lengths)

StopID_Runs$starts <- ifelse(is.na(lag(StopID_Runs$ends)
                                  ),
                             1,
                             lag(StopID_Runs$ends) + 1
                            )

str(StopID_Runs)
# class(StopID_Runs)
# 
# StopID_Runs_df <- data.frame(unclass(StopID_Runs))
# str(StopID_Runs_df)
# class(StopID_Runs_df)
# rm(StopID_Runs_df)

Trying to link data on RunsGroups with the original data (AllDays_Sorted). The goal is to select only one record per RunsGroup - that being the record with the longest Dwell_Time.

I attempted this computation using both data.frames (dplyr) and data.tables (data.table). However, with 2,809,062 rows in one dataset and 3,119,443 rows in the other dataset, the current computation time is over 5 days…so I’m trying a different strategy to only select the first record in a run.


# Create a RunsGroup variable for each run
# StopID_Runs_df$RunsGroup <- paste0("g", seq(1:nrow(StopID_Runs_df)
#                                            )
#                                   )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# StopID_Runs_df <- StopID_Runs_df %>% 
#   mutate(RowNum = row_number()
#         )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# 
# # Converting to data.tables for, hopefully, improved performance (speed) in computation
# StopID_Runs_dt <- data.table(StopID_Runs_df)
# setkey(StopID_Runs_dt, RowNum)
# str(StopID_Runs_dt)
# 
# AllDays_Sorted_dt <- data.table(AllDays_Sorted)
# setkey(AllDays_Sorted_dt, RowNum_OG)
# str(AllDays_Sorted_dt)
# # rm(AllDays_Sorted_dt)
# 
# 
# # Actual loop to perform the computations and link to original data (AllDays_Sorted_dt)
# GroupData <- list()
# for(i in 1:nrow(StopID_Runs_dt)
#    ) {
#   assign(paste0("group_", i),
#            StopID_Runs_dt[RowNum == i, RunsGroup]
#           )
# 
#     #####  The code below is the same code as above, but done with dplyr  #####
# 
#     # assign(paste0("group_", i),
#   #        filter(StopID_Runs_df,
#   #               RowNum == i
#   #              ) %>% 
#   #          select(RunsGroup)
#   #       )
# 
#   assign(paste0("group_", i, "_start"),
#          StopID_Runs_dt[RowNum == i, starts]
#         )
# 
#   assign(paste0("group_", i, "_end"),
#          StopID_Runs_dt[RowNum == i, ends]
#         )
# 
#   assign(paste0("group_", i, "_rows"),
#          AllDays_Sorted_dt[RowNum_OG >= as.numeric(get(paste0("group_", i, "_start")
#                                                       )
#                                                   ) &
#                            RowNum_OG <= as.numeric(get(paste0("group_", i, "_end")
#                                                       )
#                                                   ),
#                            RunsGroup := as.character(get(paste0("group_", i)
#                                                         )
#                                                     )
#                           ]
# 
#     #####  The code below is the same as the code above, but done with dplyr  #####
# 
#          # filter(AllDays_Sorted,
#          #        between(RowNum_OG,
#          #                as.numeric(get(paste0("group_", i, "_start")
#          #                              )
#          #                          ),
#          #                as.numeric(get(paste0("group_", i, "_end")
#          #                              )
#          #                          )
#          #               )
#          #       ) %>% 
#          #   mutate(RunsGroup = as.character(get(paste0("group_", i)
#          #                                     )
#          #                                 )
#          #        )
#         )
# 
#   GroupData[[i]] <- get(paste0("group_", i, "_rows"))
# 
#   message("Processing Group ", i, " of 2,809,062")
# }
# 
# 
# GroupData_df <- rbind.fill(GroupData)
# str(GroupData_df)
# head(GroupData_df)
# tail(GroupData_df)
# # rm(GroupData_df)
# 
# 
# group_1
# group_1_start
# group_1_end
# group_1_rows
# group_2_rows
# group_3_rows
# group_50_rows
# str(group_50_rows)
# group_2809062_rows
# GroupData[[1]]
# GroupData[[50]]
# 
# 
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# 
# # head(StopID_Runs$starts, 20)
# # head(AllDays_NewOrder$Stop_ID, 20)
# # 
# # 
# # dat <- as.data.frame(c(1,1,7,7,7,9,6,8,2,2,2,1,1,1,1,1))
# # colnames(dat)[1] <- "dat"
# # r <- rle(dat$dat)
# # dat$run <- rep(r$lengths, r$lengths)
# # dat$runLag <- lag(dat$run)
# # dat$cond <- rep(r$values, r$lengths)
# # dat
# # View(dat)

When consecutive Stop_ID occurrs, only take the first occurrence. This is done because the computation time to select only the record with the longest Dwell_Time for each run was too long (over 5 days).

This is probably less than ideal with regards to Dwell_Time, but should not make much difference for calculations of travel time, speed, etc.


AllDays_FirstStopID <- AllDays_Zips[StopID_Runs$starts, ]

dim(AllDays_Zips)
dim(AllDays_FirstStopID)

nrow(AllDays_Zips) - nrow(AllDays_FirstStopID)

rm(AllDays_Zips, StopID_Runs)
str(AllDays_FirstStopID)

Feature engineering.

Creating new variables.


AllDays_AddVars <- mutate(AllDays_FirstStopID,
                          Odometer_Distance_Mi = Odometer_Distance / 5280, #5,280 feet in 1 mile
                          Dwell_Time2 = as.numeric(Departure_Time - Event_Time),
                          Event_Time_Yr = as.integer(year(Event_Time)),
                          Event_Time_Mth = as.integer(month(Event_Time)),
                          Event_Time_Date = day(Event_Time),
                          Event_Time_Day = wday(Event_Time, label = TRUE),
                          Event_Time_Hr = hour(Event_Time),
                          Event_Time_Min = minute(Event_Time),
                          Event_Time_HrGroup = factor(ifelse(Event_Time_Hr < 3,
                                                             "Group0_2",
                                                      ifelse(Event_Time_Hr < 6,
                                                             "Group3_5",
                                                      ifelse(Event_Time_Hr < 9,
                                                             "Group6_8",
                                                      ifelse(Event_Time_Hr < 12,
                                                             "Group9_11",
                                                      ifelse(Event_Time_Hr < 15,
                                                             "Group12_14",
                                                      ifelse(Event_Time_Hr < 18,
                                                             "Group15_17",
                                                      ifelse(Event_Time_Hr < 21,
                                                             "Group18_20",
                                                      ifelse(Event_Time_Hr < 24,
                                                             "Group21_23"
                                                            )))))))),
                                                         levels = c("Group0_2",
                                                                    "Group3_5",
                                                                    "Group6_8",
                                                                    "Group9_11",
                                                                    "Group12_14",
                                                                    "Group15_17",
                                                                    "Group18_20",
                                                                    "Group21_23"
                                                                   ),
                                                         ordered = TRUE
                                                     )
                         )

rm(AllDays_FirstStopID)
str(AllDays_AddVars)


# group_by(AllDays_AddVars,
#          Event_Time_HrGroup
#         ) %>% 
#   summarise(Cnts = n()
#            )


# View(head(filter(AllDays_AddVars,
#                  Event_Time_Hr == 0
#                 ),
#           50
#          )
#     )

# View(head(AllDays_AddVars, 50))

Feature engineering.

Creating more variables. Creating a BusEvent row number for future identification purposes. Then, creating various variables to analyze distance traveled and speed.


AllDays_BusDay <- group_by(AllDays_AddVars,
                           Bus_ID,
                           Event_Time_Date
                          ) %>% 
  mutate(BusDay_EventNum = row_number(),  # used to identify Bus movements on a particular date
         
         Route_Lag1 = lag(Route),  # used in future analyses to identify Route changes
         RouteAlt_Lag1 = lag(RouteAlt),  # used in future analyses to identify RouteAlt (direction) changes
         
         Odometer_Distance_Lag1 = lag(Odometer_Distance),
         
         Latitude_L1 = lag(Latitude),
         Longitude_L1 = lag(Longitude),
         # Lat_Radian = Latitude*pi/180,
         # Long_Radian = Longitude*pi/180,
         # Lat_Radian_L1 = lag(Lat_Radian),
         # Long_Radian_L1 = lag(Long_Radian),
         
         # accounting for potential negative distances
         TravelDistance_Ft = ifelse(Odometer_Distance > Odometer_Distance_Lag1,
                                    Odometer_Distance - Odometer_Distance_Lag1,
                                    NA
                                   ),
         TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
         
         # TravelDistance_Mi2 = gcd.hf(long1 = Long_Radian_L1,
         #                             lat1 = Lat_Radian_L1,
         #                             long2 = Long_Radian,
         #                             lat2 = Lat_Radian
         #                            ),
         
         TravelDistance_Mi_Hvrs = 
                              # ifelse((is.na(Longitude_L1) | is.na(Latitude_L1)
                              #        ),
                              #        NA,
                              distHaversine(cbind(Longitude_L1, Latitude_L1),
                                            cbind(Longitude, Latitude)
                                           ) * 0.000621371, # 0.000621371 miles = 1 meter
         
         # accounting for potential negative times
         TravelTime_Sec = as.numeric(ifelse(Event_Time > lag(Departure_Time),
                                            Event_Time - lag(Departure_Time),
                                            NA
                                           )
                                    ),
         TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
         
         # accounting for potential negative or zero travel times
         SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
                               TravelDistance_Mi / TravelTime_Hr,
                               NA
                              ),
         
         Start_ID = lag(StopID_Clean),
         Start_Desc = lag(Stop_Desc),
         StartStop_ID = ifelse(is.na(Start_ID),
                               paste("NULL", StopID_Clean, sep = "--"),
                               paste(Start_ID, StopID_Clean, sep = "--")
                              )
        ) %>% 
  as.data.frame()


rm(AllDays_AddVars)
str(AllDays_BusDay)

# summary(AllDays_BusDay)

# View(tail(AllDays_BusDay, 50))

Inspecting for issues with StartStop_ID (where the value is either NA or contains NULL). They ONLY exist when BusDay_EventNum = 1 (which is by design). So everything looks OK.


View(group_by(AllDays_BusDay,
              StartStop_ID
             ) %>% 
       summarise(
         Cnt = n()
       ) %>% 
       arrange(desc(Cnt)
              )
    )

View(filter(AllDays_BusDay,
            (is.na(StartStop_ID) |
              str_detect(StartStop_ID, "NULL")
            ) &
              BusDay_EventNum != 1
           )
    )

Stats (quantiles) overall for TravelDistance_Mi.


Quantiles_dt <- AllDays_BusDay %>% 
  mutate(TD_Mi_q2 = quantile(x = TravelDistance_Mi, probs = 0.02, na.rm = TRUE),
         TD_Mi_q98 = quantile(x = TravelDistance_Mi, probs = 0.98, na.rm = TRUE),
         TT_Sec_q2 = quantile(x = TravelTime_Sec, probs = 0.02, na.rm = TRUE),
         TT_Sec_q98 = quantile(x = TravelTime_Sec, probs = 0.98, na.rm = TRUE),
         TT_Hr_q2 = quantile(x = TravelTime_Hr, probs = 0.02, na.rm = TRUE),
         TT_Hr_q98 = quantile(x = TravelTime_Hr, probs = 0.98, na.rm = TRUE)
        ) %>% 
  data.table()


Stats <- Quantiles_dt %>% 
  mutate(TD_Mi_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Mean_F = mean(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                             na.rm = TRUE
                            ),
         TD_Mi_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Med_F = median(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                              na.rm = TRUE
                             ),
         TD_Mi_Cnt = sum(!is.na(TravelDistance_Mi)
                        ),
         TD_Mi_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98]
                                 )
                          ),
            
         TT_Sec_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Mean_F = mean(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                              na.rm = TRUE
                             ),
         TT_Sec_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Med_F = median(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                               na.rm = TRUE
                              ),
         TT_Sec_Cnt = sum(!is.na(TravelTime_Sec)
                         ),
         TT_Sec_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98]
                                   )
                           ),

         TT_Hr_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Mean_F = mean(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                             na.rm = TRUE
                            ),
         TT_Hr_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Med_F = median(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                              na.rm = TRUE
                             ),
         TT_Hr_Cnt = sum(!is.na(TravelTime_Hr)
                        ),
         TT_Hr_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98]
                                 )
                          )
        ) %>% 
  data.frame()

rm(AllDays_BusDay)
rm(Quantiles_dt)
str(Stats)
# View(head(Stats, 50))

Stats for StartStop_ID.


Quantiles_SS_dt <- group_by(Stats,
                            StartStop_ID
                           ) %>% 
  mutate(TD_Mi_SS_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SS_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SS_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SS_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SS_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SS_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt <- group_by(Quantiles_SS_dt,
                       StartStop_ID
                      ) %>% 
  mutate(TD_Mi_SS_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Mean_F = mean(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                na.rm = TRUE
                               ),
         TD_Mi_SS_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Med_F = median(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                 na.rm = TRUE
                                ),
         TD_Mi_SS_Cnt = sum(!is.na(TravelDistance_Mi)
                           ),
         TD_Mi_SS_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95]
                                    )
                             ),
            
         TT_Sec_SS_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Mean_F = mean(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Sec_SS_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Med_F = median(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                  na.rm = TRUE
                                 ),
         TT_Sec_SS_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SS_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95]
                                     )
                              ),

         TT_Hr_SS_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Mean_F = mean(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                na.rm = TRUE
                               ),
         TT_Hr_SS_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Med_F = median(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Hr_SS_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SS_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95]
                                    )
                             )
        ) %>% 
  data.frame()

rm(Stats)
rm(Quantiles_SS_dt)
str(Stats_StSt)
# View(head(Stats_StSt, 50))

Stats for StartStop_ID with Event_Time_HrGroup.


Quantiles_SSHG_dt <- group_by(Stats_StSt,
                              StartStop_ID,
                              Event_Time_HrGroup
                             ) %>% 
  mutate(TD_Mi_SSHG_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SSHG_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SSHG_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SSHG_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SSHG_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SSHG_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt_HrGrp <- group_by(Quantiles_SSHG_dt,
                             StartStop_ID,
                             Event_Time_HrGroup
                            ) %>% 
  mutate(TD_Mi_SSHG_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Mean_F = mean(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TD_Mi_SSHG_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Med_F = median(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TD_Mi_SSHG_Cnt = sum(!is.na(TravelDistance_Mi)
                             ),
         TD_Mi_SSHG_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95]
                                      )
                               ),
            
         TT_Sec_SSHG_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Mean_F = mean(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Sec_SSHG_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Med_F = median(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                    na.rm = TRUE
                                   ),
         TT_Sec_SSHG_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SSHG_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95]
                                       )
                                ),

         TT_Hr_SSHG_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Mean_F = mean(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TT_Hr_SSHG_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Med_F = median(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Hr_SSHG_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SSHG_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95]
                                      )
                               )
        ) %>% 
  data.frame()

rm(Stats_StSt)
rm(Quantiles_SSHG_dt)
str(Stats_StSt_HrGrp)
# View(head(Stats_StSt_HrGrp, 50))

Feature engineering.

Creating a BusEventRoute row number, and a RouteAlt_Lag1 indicator for future identification purposes.


# rm(Quantiles_dt)
# rm(Quantiles_SS_dt)
# rm(AllDays_BusDay)
# rm(Quantiles_SSHG_dt)
# rm(Stats_StSt)

# AllDays_BusDayRoute <- group_by(Stats_StSt_HrGrp,
#                                 Bus_ID,
#                                 Event_Time_Date,
#                                 Route
#                                ) %>% 
#   mutate(RouteAlt_Lag2 = lag(RouteAlt)  # used in future analyses to identify RouteAlt (direction) changes
#          
#          # Odometer_Distance_Lag1 = lag(Odometer_Distance),
#          # 
#          # # accounting for potential negative distances
#          # TravelDistance_Ft = ifelse(Odometer_Distance >= Odometer_Distance_Lag1,
#          #                            Odometer_Distance - Odometer_Distance_Lag1,
#          #                            NA
#          #                           ),
#          # TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
#          # 
#          # # accounting for potential negative times
#          # TravelTime_Sec = as.numeric(ifelse(Event_Time >= lag(Departure_Time),
#          #                                    Event_Time - lag(Departure_Time),
#          #                                    NA
#          #                                   )
#          #                            ),
#          # TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
#          # 
#          # # accounting for potential negative or zero travel times
#          # SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
#          #                       TravelDistance_Mi / TravelTime_Hr,
#          #                       NA
#          #                      )
#         ) %>% 
#   data.frame()
# 
# rm(Stats_StSt_HrGrp)
# str(AllDays_BusDayRoute)

Feature engineering.

Calculating a variable to know if the RouteAlt changed. Could be useful in helping identifying weirdness in calculated distances and speeds.


# rm(Stats_StSt_HrGrp)

AllDays_DirChange <- Stats_StSt_HrGrp %>%  # AllDays_BusDayRoute %>% 
  mutate(RteChange = ifelse(Route == Route_Lag1,
                            "Same",
                            "Change"
                           ),
         RteChange2 = factor(ifelse(is.na(RteChange),
                                    "Change",
                                    RteChange
                                   )
                            ),
         DirChange = ifelse(RouteAlt == RouteAlt_Lag1,
                            "Same",
                            "Change"
                           ),
         DirChange2 = factor(ifelse(is.na(DirChange),
                                    "Change",
                                    DirChange
                                   )
                            )
        )

# rm(AllDays_BusDayRoute)
rm(Stats_StSt_HrGrp)
str(AllDays_DirChange)

View(filter(AllDays_DirChange,
            between(RowNum_OG, 2570060, 2570080)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Re-ordering the variables to ease with comprehension.


AllDays_NewOrder <-  select(AllDays_DirChange,
                            RowNum_OG,
                            UniqueLatLng,
                            group,
                            StartStop_ID,
                            BusDay_EventNum,
                            Bus_ID,
                            Route,
                            RteChange2,
                            RouteAlt,
                            # RouteAlt_Lag1,
                            DirChange2,
                            Route_Direction,
                            Stop_Sequence,
                            Start_ID,
                            Start_Desc,
                            # Stop_ID,
                            StopID_Clean,
                            StopID_Indicator,
                            Stop_Desc,
                            countryCode,
                            Stop_State,
                            Stop_County,
                            Stop_City,
                            Stop_Zip,
                            Event_Type,
                            Event_Description,
                            Event_Time_Yr,
                            Event_Time_Mth,
                            Event_Time_Date,
                            Event_Time_Day,
                            Event_Time_Hr,
                            Event_Time_HrGroup,
                            Event_Time_Min,
                            Event_Time,
                            Departure_Time,
                            Dwell_Time,
                            Dwell_Time2,
                            Delta_Time,
                            Latitude,
                            Longitude,
                            Heading,
                            Odometer_Distance,
                            Odometer_Distance_Lag1,
                            Odometer_Distance_Mi,
                            TravelDistance_Ft,
                            TravelDistance_Mi,
                            TravelDistance_Mi_Hvrs,
                            TD_Mi_q2,
                            TD_Mi_q98,
                            TD_Mi_SS_q5,
                            TD_Mi_SS_q95,
                            TD_Mi_SSHG_q5,
                            TD_Mi_SSHG_q95,
                            TD_Mi_Mean,
                            TD_Mi_Mean_F,
                            TD_Mi_SS_Mean,
                            TD_Mi_SS_Mean_F,
                            TD_Mi_SSHG_Mean,
                            TD_Mi_SSHG_Mean_F,
                            TD_Mi_Med,
                            TD_Mi_Med_F,
                            TD_Mi_SS_Med,
                            TD_Mi_SS_Med_F,
                            TD_Mi_SSHG_Med,
                            TD_Mi_SSHG_Med_F,
                            TD_Mi_Cnt,
                            TD_Mi_Cnt_F,
                            TD_Mi_SS_Cnt,
                            TD_Mi_SS_Cnt_F,
                            TD_Mi_SSHG_Cnt,
                            TD_Mi_SSHG_Cnt_F,
                            TravelTime_Sec,
                            TT_Sec_q2,
                            TT_Sec_q98,
                            TT_Sec_SS_q5,
                            TT_Sec_SS_q95,
                            TT_Sec_SSHG_q5,
                            TT_Sec_SSHG_q95,
                            TT_Sec_Mean,
                            TT_Sec_Mean_F,
                            TT_Sec_SS_Mean,
                            TT_Sec_SS_Mean_F,
                            TT_Sec_SSHG_Mean,
                            TT_Sec_SSHG_Mean_F,
                            TT_Sec_Med,
                            TT_Sec_Med_F,
                            TT_Sec_SS_Med,
                            TT_Sec_SS_Med_F,
                            TT_Sec_SSHG_Med,
                            TT_Sec_SSHG_Med_F,
                            TT_Sec_Cnt,
                            TT_Sec_Cnt_F,
                            TT_Sec_SS_Cnt,
                            TT_Sec_SS_Cnt_F,
                            TT_Sec_SSHG_Cnt,
                            TT_Sec_SSHG_Cnt_F,
                            TravelTime_Hr,
                            TT_Hr_q2,
                            TT_Hr_q98,
                            TT_Hr_SS_q5,
                            TT_Hr_SS_q95,
                            TT_Hr_SSHG_q5,
                            TT_Hr_SSHG_q95,
                            TT_Hr_Mean,
                            TT_Hr_Mean_F,
                            TT_Hr_SS_Mean,
                            TT_Hr_SS_Mean_F,
                            TT_Hr_SSHG_Mean,
                            TT_Hr_SSHG_Mean_F,
                            TT_Hr_Med,
                            TT_Hr_Med_F,
                            TT_Hr_SS_Med,
                            TT_Hr_SS_Med_F,
                            TT_Hr_SSHG_Med,
                            TT_Hr_SSHG_Med_F,
                            TT_Hr_Cnt,
                            TT_Hr_Cnt_F,
                            TT_Hr_SS_Cnt,
                            TT_Hr_SS_Cnt_F,
                            TT_Hr_SSHG_Cnt,
                            TT_Hr_SSHG_Cnt_F,
                            SpeedAvg_Mph
                           )

rm(AllDays_DirChange)
str(select(AllDays_NewOrder,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(AllDays_NewOrder)

# View(head(AllDays_NewOrder, 500))
# View(tail(AllDays_NewOrder, 500))

Summarizing the data to help spot anomolies.


View(group_by(AllDays_NewOrder,
              Stop_City) %>% 
       summarise(Cnt_Num = n(),
                 Cnt_Pct = 100*Cnt_Num / (nrow(AllDays_NewOrder)
                                         )
                ) %>% 
       arrange(desc(Cnt_Num))
)

summary(AllDays_NewOrder)

Investigation of TravelDistance_Mi.

View(TravDistMi_Pctiles): 99% of TravelDistance_Mi are about 1 mile or less…but some weird TravelDistance_Mi values (e.g., 584 miles traveled) exist.


TravDistMi_Ntile <- as.data.frame(AllDays_NewOrder$TravelDistance_Mi) %>% 
  mutate(#Pctile = ntile(AllDays_NewOrder$TravelDistance_Mi, 100),
         #MinR = min_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR = percent_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR_Round = round(PctR, 2)
        ) 

colnames(TravDistMi_Ntile)[1] <- "TravelDistance_Mi"
# str(TravDistMi_Ntile)

TravDistMi_Ntile_Rows <- nrow(TravDistMi_Ntile)

# View(tail(TravDistMi_Ntile, 500))


TravDistMi_Pctiles <- group_by(TravDistMi_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravDistMiAtPctile = min(TravelDistance_Mi),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravDistMi_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

rm(TravDistMi_Ntile)
rm(TravDistMi_Ntile_Rows)

View(TravDistMi_Pctiles)
TravDistMi_Pctiles

Investigation of TravelDistance_Mi.

Why are some TravelDistance_Mi “NA”? It looks like partially because the records are the first trip of the day (for that bus), so I purposefully set the distance to “NA”. Another reason is due to the odometer recording a value less than the previous odometer recording. In most cases, I have no explanation for this - though I have observed about 67% of all instances where TravelDistance_Mi is NA (other than because it’s the first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


# View(head(AllDays_NewOrder, 500))

View(filter(AllDays_NewOrder,
            BusDay_EventNum != 1 # When BusDay_EventNum == 1, TravelDistance_Mi is NA by design (don't want to calculate distance based on yesterday's position)
           ) %>% 
       group_by(StartStop_ID) %>% 
       summarise(Cnts = sum(is.na(TravelDistance_Mi)
                           )
                ) %>% 
       arrange(desc(Cnts)
              )
    )

View(filter(AllDays_NewOrder,
            StartStop_ID == "1000245--1000211"
           ) %>% 
       select(RowNum_OG,
              StartStop_ID,
              Event_Time,
              Event_Time_HrGroup,
              Bus_ID,
              TravelDistance_Mi,
              TravelDistance_Mi_Hvrs,
              TD_Mi_SS_Mean,
              TD_Mi_SS_Mean_F,
              TD_Mi_SSHG_Mean,
              TD_Mi_SSHG_Mean_F,
              TD_Mi_SS_Med,
              TD_Mi_SS_Med_F,
              TD_Mi_SSHG_Med,
              TD_Mi_SSHG_Med_F,
              TD_Mi_SS_Cnt,
              TD_Mi_SS_Cnt_F,
              TD_Mi_SSHG_Cnt,
              TD_Mi_SSHG_Cnt_F
              ) %>% 
       mutate(Ratio_MeanToHvrs = TD_Mi_SS_Mean / TravelDistance_Mi_Hvrs) %>% 
       arrange(Event_Time)
    )

View(filter(AllDays_NewOrder,
            is.na(TravelDistance_Mi)
           )
    )

# These records are NA becuase the record is the first record of the day (the Event_Time_Date)
View(filter(AllDays_NewOrder,
            between(RowNum_OG, 326, 346) | # 336
              between(RowNum_OG, 591, 611) | # 601
              between(RowNum_OG, 845, 865) # 855
           )
    )

Investigation of TravelDistance_Mi.

These records are NA becuase the current record odometer is less than the previous record odometer. Theoretically, this should NOT happen. Me: it appears that about 67% of all instances where TravelDistance_Mi is NA (other than because it’s th first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


View(filter(AllDays_NewOrder,
            between(RowNum_OG, 194, 214) | # 204
              between(RowNum_OG, 440, 460) | # 450
              between(RowNum_OG, 478, 498) | # 488
              between(RowNum_OG, 510, 530) # 520
           )
    )

TestTable <- filter(AllDays_NewOrder,
                    BusDay_EventNum != 1
                   ) %>% 
  mutate(TravelDistance_NA = as.factor(ifelse(is.na(TravelDistance_Mi),
                                              "True",
                                              "False"
                                             )
                                      )
        ) %>%
  group_by(DirChange2, TravelDistance_NA) %>%
  summarise(TravDistMi_NACnts = n()
           )

# TestTable

TestTable_Spread <- as.data.frame(spread(TestTable,
                                         TravelDistance_NA,
                                         TravDistMi_NACnts
                                        )
                                 ) %>% 
  select(False,
         True
        )

row.names(TestTable_Spread) <- c("Change", "Same")
# str(TestTable_Spread)
# TestTable_Spread

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Let’s look at just the TravelDistance_Mi values that are NOT “NA”.


rm(TestTable, TestTable_Spread)

TravelDistance_Mi_NoNA <- filter(AllDays_NewOrder,
                                 # TravelDistance_Mi != 0 &
                                 !is.na(TravelDistance_Mi)
                                )

dim(AllDays_NewOrder)
dim(TravelDistance_Mi_NoNA)
nrow(AllDays_NewOrder) - nrow(TravelDistance_Mi_NoNA)

str(TravelDistance_Mi_NoNA)
summary(TravelDistance_Mi_NoNA)

Investigation of TravelDistance_Mi.

Let’s plot just the TravelDistance_Mi values that are NOT “NA”.


TravDistMi_HistDen <- ggplot(select(TravelDistance_Mi_NoNA,
                                    TravelDistance_Mi
                                   ),
                             aes(x = TravelDistance_Mi,
                                 y = ..density..
                                )
                            ) +
  geom_histogram(binwidth = 0.05, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 4.0)
                 ) +
  labs(title = "Variation in Distance Between Stops",
       x = "Travel Distance (miles)",
       y = "Density"
      )

TravDistMi_HistDen

Investigation of TravelDistance_Mi.

Looking at the extremely large TravelDistance_Mi values. Some (aprox 27%) of TravelDistance_Mi values > 1 mile are when the DirChange2 changes…but what about the other ~73%?


rm(TravelDistance_Mi_NoNA)

# examples of weirdly large TravelDistance_Mi
View(filter(AllDays_NewOrder,
            TravelDistance_Mi > 1.1587121212 # 1.1587121212 is the 99th percentile
           ) %>% 
       arrange(desc(TravelDistance_Mi)
              )
    )


# Why are these extremes?  Airports?  Bus collection points?
View(filter(AllDays_NewOrder,
              between(RowNum_OG, 494044, 494064) | # 494054
              between(RowNum_OG, 494273, 494293) | # 494283
              between(RowNum_OG, 494626, 494646) | # 494636
              between(RowNum_OG, 1610156, 1610176) | # 1610166
              between(RowNum_OG, 2073074, 2073094) # 2073084
           )
    )

# Before Removing Runs
# View(filter(AllDays_Sorted,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

# After Removing Runs
# View(filter(AllDays_FirstStopID,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

Investigation of TravelDistance_Mi.

Any relation with DirChange2? Doesn’t look as if this is so.


ExtremeTravDist <- filter(AllDays_NewOrder,
                          !is.na(TravelDistance_Mi)
                         ) %>% 
  mutate(TravDist_Extreme = ifelse(TravelDistance_Mi > 1.1587121212, # 1.1587121212 is the 99th percentile
                                   "True",
                                   "False"
                                  )
                          ) %>% 
  group_by(DirChange2, TravDist_Extreme) %>% 
  summarise(TravDistMI_ExtCnts = n()
           )

# ExtremeTravDist


ExtremeTravDist_Spread <- as.data.frame(spread(ExtremeTravDist,
                                               TravDist_Extreme,
                                               TravDistMI_ExtCnts
                                              )
                                       ) %>% 
  select(False,
         True
        )

row.names(ExtremeTravDist_Spread) <- c("Change", "Same")
# str(ExtremeTravDist_Spread)
# ExtremeTravDist_Spread

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Looking at specific buses and StartStop_ID.


rm(ExtremeTravDist, ExtremeTravDist_Spread)

View(arrange(group_by(AllDays_NewOrder,
                      Bus_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )


# example of extremely small TravelDistance_Mi values (looks like the odometer wasn't functioning)
View(filter(AllDays_NewOrder,
            Bus_ID == 6111 |
              Bus_ID == 7201 |
              Bus_ID == 8058
           ) %>% 
       arrange(Bus_ID, Event_Time)
    )


View(arrange(group_by(AllDays_NewOrder,
                      StartStop_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )

# example of extremely large TravelDistance_Mi values...no idea why...
View(filter(AllDays_NewOrder,
            StartStop_ID == "1003665--12" |
              StartStop_ID == "1003665--5001925" |
              StartStop_ID == "3001038--3002565"
           ) %>% 
       arrange(StartStop_ID, Event_Time)
    )

Investigation of TravelDistance_Mi & TravelDistance_Mi_New.

If TravelDisntace_Mi is below the 5th percentile for that StartStop_ID, or if TravelDisntace_Mi is above the 95th percentile for that StartStop_ID, or if TravelDistance_Mi is NA (when the BusDay_EventNum !=1), consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TD_Mi_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


# View(tail(AllDays_NewOrder, 500))

AllDays_NewTravelDist <- 
  mutate(AllDays_NewOrder,
         TravelDistance_Mi_New = ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F >= 20,
                                        TD_Mi_SSHG_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt_F >= 20,
                                        TD_Mi_SS_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SS_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt >= 20,
                                        TD_Mi_SS_Mean,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs != 0,
                                        TravelDistance_Mi_Hvrs,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs == 0,
                                        TD_Mi_SS_Mean,
                                        TravelDistance_Mi
                                       ))))),
         TravelDistance_Mi_New_Label = 
           factor(ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F >= 20,
                         "TD_Mi_SSHG_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F < 20 &
                           TD_Mi_SS_Cnt_F >= 20,
                         "TD_Mi_SS_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SS_Cnt_F < 20 &
                           TD_Mi_SS_Cnt >= 20,
                         "TD_Mi_SS_Mean",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs != 0,
                         "TravelDistance_Mi_Hvrs",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs == 0,
                         "TD_Mi_SS_Mean",
                         "TravelDistance_Mi"
                        )))))
                 ),
         TravelDistance_Mi_NewHvrs = ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                                              TravelDistance_Mi_Hvrs != 0 &
                                              (TravelDistance_Mi_New < TD_Mi_q2 |
                                                 TravelDistance_Mi_New > TD_Mi_q98
                                              ),
                                            TravelDistance_Mi_Hvrs,
                                            TravelDistance_Mi_New
                                           ),
         TravelDistance_Mi_NewHvrs_Label =
           factor(ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                           TravelDistance_Mi_Hvrs != 0 &
                           (TravelDistance_Mi_New < TD_Mi_q2 |
                              TravelDistance_Mi_New > TD_Mi_q98
                           ),
                         "TravelDistance_Mi_Hvrs",
                         as.character(TravelDistance_Mi_New_Label)
                        )
                 ),
         SpeedAvg_Mph_NewHvrs = TravelDistance_Mi_NewHvrs / TravelTime_Hr
        )

str(AllDays_NewTravelDist)

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Quick summary and then correlation calculation.


rm(AllDays_NewOrder)


# 38 rows meet this criteria anymore  --  appears to be the case when both the Lat Long calculations, and the TravelDistance calculations did not function properly.
View(filter(AllDays_NewTravelDist,
            is.na(TravelDistance_Mi_New) &
              BusDay_EventNum != 1
           )
    )

View(AllDays_NewTravelDist %>% 
       arrange(desc(TravelDistance_Mi_New)) %>% 
       head(500)
    )

summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


cor(select(AllDays_NewTravelDist,
           TravelDistance_Mi,
           TravelDistance_Mi_Hvrs,
           TravelDistance_Mi_New,
           TravelDistance_Mi_NewHvrs
          ),
    use = "pairwise.complete.obs"
  )

Investigation of TravelDistance_Mi_NewHvrs_Label & TravelDistance_Mi_NewHvrs_Label.

Show how the labels changed.


group_by(AllDays_NewTravelDist,
         TravelDistance_Mi_New_Label,
         TravelDistance_Mi_NewHvrs_Label
        ) %>% 
  summarise(CntNum = n(),
            CntPct = format(CntNum / nrow(AllDays_NewTravelDist),
                            scientific = 9999
                           )
           ) %>% 
  arrange(desc(CntPct)
         )

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing the two methods of calculating TravelDistance_Mi.

First, let’s get create a function to plot the liner model equation.


lm_eqn <- function(df, y, x){
  m <- lm(y ~ x, df)
  
  l <- list(a = format(coef(m)[1], digits = 2),
            b = format(abs(coef(m)[2]), digits = 2),
            s1 = ifelse(test = coef(m)[2] > 0,
                        yes = "+",
                        no = "-"
                       ),
            r2 = format(summary(m)$r.squared,
                        digits = 3
                       )
           )
  
  eq <- substitute(italic(y) == a~~s1~~b %.% italic(x)*","~~italic(r)^2~"="~r2,
                   l
                  )
  
  as.character(as.expression(eq)
              )             
}

Investigation of TravelDistance_Mi & TravelDistance_Mi_NewHvrs.

Scatter plot (using a 10% sample to making plotting time faster and to reduce un-needed data in the “same” splot).


set.seed(123456789)
AllDays_NewTravelDist_10Pct <- filter(AllDays_NewTravelDist,
                                      !is.na(TravelDistance_Mi_NewHvrs) &
                                        !is.na(TravelDistance_Mi)
                                     ) %>% 
  rename(DistMethod = TravelDistance_Mi_NewHvrs_Label) %>% 
  sample_frac(0.1)


TravDist_MiVsCalc <- ggplot(select(AllDays_NewTravelDist_10Pct,
                                   TravelDistance_Mi_NewHvrs,
                                   TravelDistance_Mi,
                                   DistMethod
                                  ),
                            aes(x = TravelDistance_Mi,
                                y = TravelDistance_Mi_NewHvrs,
                                colour = DistMethod
                               )
                           ) +
  scale_colour_manual(values = c("red","blue", "green", "orange", "black")
                     ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  geom_abline(intercept = 0, slope = 1, colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 1.5)
                 ) +
  scale_x_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  scale_y_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  theme(legend.position = "bottom", #c(0.85, 0.40),
        legend.text = element_text(size = 6)
       ) +
  annotate(label = lm_eqn(df = AllDays_NewTravelDist_10Pct,
                          x = AllDays_NewTravelDist_10Pct$TravelDistance_Mi,
                          y = AllDays_NewTravelDist_10Pct$TravelDistance_Mi_NewHvrs
                         ),
           # x = 62,
           # y = 20,
           x = 0.70,
           y = 0.00,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  annotate(label = "Reference Line (slope = 1)",
           # x = 16,
           # y = 30,
           x = 0.80,
           y = 1.05,
           geom = "text",
           size = 3,
           colour = "red"
          ) +
  labs(title = "TravelDistance_Mi vs. TravelDistance_Mi_NewHvrs",
       x = "TravelDistance_Mi",
       y = "TravelDistance_Mi_NewHvrs"
      )
# +
#   geom_jitter()

TravDist_MiVsCalc

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing test with rbokeh.


TravDist_MiVsCalc_Bokeh <- figure(data = select(AllDays_NewTravelDist_10Pct,
                                                TravelDistance_Mi_NewHvrs,
                                                TravelDistance_Mi,
                                                DistMethod
                                               ),
                                  xlim = c(0, 1.5),
                                  ylim = c(0, 1.5),
                                  legend_location = "bottom_right"
                                 ) %>% 
  ly_points(x = TravelDistance_Mi,
            y = TravelDistance_Mi_NewHvrs,
            color = DistMethod,
            hover = c(TravelDistance_Mi_NewHvrs, TravelDistance_Mi, DistMethod)
           ) %>% 
  ly_abline(a = 0, b = 1, color = "red")

TravDist_MiVsCalc_Bokeh

Investigation of TravelDistance_Mi_New.

Calculating the minimum TravelDistance_Mi_New value at each percentile.


rm(TravDist_MiVsCalc_Bokeh)
rm(AllDays_NewTravelDist_10Pct)


summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


TravDistMiN_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          TravelDistance_Mi_New_Label,
                                          # TravelDistance_Mi_NewHvrs_Label,
                                          TravelDistance_Mi_New
                                          # TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         # PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         PctR_Round_N = round(PctR_N, 2)
         # PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiN_Ntile)
# View(head(TravDistMiN_Ntile, 500))

TravDistMiN_Ntile_Rows <- nrow(TravDistMiN_Ntile)

# View(tail(TravDistMiN_Ntile, 500))


TravDistMiN_Pctiles <- group_by(TravDistMiN_Ntile,
                                PctR_Round_N
                               ) %>% 
  summarise(
    MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    # MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    # CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    PctsAtPctile_N = CntsAtPctile_N / TravDistMiN_Ntile_Rows
    # PctsAtPctile_H = CntsAtPctile_H / TravDistMiN_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP_N = cumsum(PctsAtPctile_N)
         # CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiN_Pctiles)

Investigation of TravelDistance_Mi_NewHvrs

Calculating the minimum TravelDistance_Mi_NewHvrs value at each percentile.


TravDistMiH_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          # TravelDistance_Mi_New_Label,
                                          TravelDistance_Mi_NewHvrs_Label,
                                          # TravelDistance_Mi_New,
                                          TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(# PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         # PctR_Round_N = round(PctR_N, 2),
         PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiH_Ntile)
# View(head(TravDistMiH_Ntile, 500))

TravDistMiH_Ntile_Rows <- nrow(TravDistMiH_Ntile)

# View(tail(TravDistMiH_Ntile, 500))


TravDistMiH_Pctiles <- group_by(TravDistMiH_Ntile,
                                PctR_Round_H
                               ) %>% 
  summarise(
    # MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    # CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    # PctsAtPctile_N = CntsAtPctile_N / TravDistMiH_Ntile_Rows,
    PctsAtPctile_H = CntsAtPctile_H / TravDistMiH_Ntile_Rows
  ) %>% 
  mutate(# CumSumPAtP_N = cumsum(PctsAtPctile_N),
         CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiH_Pctiles)

Join TravDistMiH_Pctiles, TravDistMiN_Pctiles, and TravDistMi_Pctiles.

~11% of rides are still showing as less than 0.1 miles of TravelDistance_Mi_NewHvrs.


rm(TravDistMiN_Ntile_Rows, TravDistMiH_Ntile_Rows, TravDistMiN_Ntile, TravDistMiH_Ntile)


# View(TravDistMi_Pctiles)
# View(TravDistMiN_Pctiles)
# View(TravDistMiH_Pctiles)

TravDistMi_Pctiles_All <- inner_join(x = TravDistMi_Pctiles,
                                     y = TravDistMiN_Pctiles,
                                     by = c("PctR_Round" = "PctR_Round_N")
                                    ) %>% 
  inner_join(y = TravDistMiH_Pctiles,
             by = c("PctR_Round" = "PctR_Round_H")
            ) %>% 
  select(PctR_Round,
         MinTravDistMiAtPctile,
         MinTDMiAtPctile_N,
         MinTDMiAtPctile_H,
         CntsAtPctile,
         CntsAtPctile_N,
         CntsAtPctile_H,
         PctsAtPctile,
         PctsAtPctile_N,
         PctsAtPctile_H,
         CumSumPAtP,
         CumSumPAtP_N,
         CumSumPAtP_H
         )

# str(TravDistMi_Pctiles_All)

rm(TravDistMi_Pctiles, TravDistMiN_Pctiles,TravDistMiH_Pctiles)


View(TravDistMi_Pctiles_All)
TravDistMi_Pctiles_All

Investigation of TravelDistance_Mi_New.

Why are there still some small or large TravelDistance_Mi_NewHvrs values.


# View(filter(AllDays_NewTravelDist,
#             !is.na(TravelDistance_Mi_NewHvrs)
#            ) %>% 
#        select(-TD_Mi_q2,
#               -TD_Mi_q98,
#               -TD_Mi_SS_q5,
#               -TD_Mi_SS_q95,
#               -TD_Mi_SSHG_q5,
#               -TD_Mi_SSHG_q95,
#               -TD_Mi_Mean,
#               -TD_Mi_Mean_F,
#               -TD_Mi_SS_Mean,
#               -TD_Mi_SS_Mean_F,
#               -TD_Mi_SSHG_Mean,
#               -TD_Mi_SSHG_Mean_F,
#               -TD_Mi_Med,
#               -TD_Mi_Med_F,
#               -TD_Mi_SS_Med,
#               -TD_Mi_SS_Med_F,
#               -TD_Mi_SSHG_Med,
#               -TD_Mi_SSHG_Med_F,
#               -TD_Mi_Cnt,
#               -TD_Mi_Cnt_F,
#               -TD_Mi_SS_Cnt,
#               -TD_Mi_SS_Cnt_F,
#               -TD_Mi_SSHG_Cnt,
#               -TD_Mi_SSHG_Cnt_F,
#               -TT_Sec_q2,
#               -TT_Sec_q98,
#               -TT_Sec_SS_q5,
#               -TT_Sec_SS_q95,
#               -TT_Sec_SSHG_q5,
#               -TT_Sec_SSHG_q95,
#               -TT_Sec_Mean,
#               -TT_Sec_Mean_F,
#               -TT_Sec_SS_Mean,
#               -TT_Sec_SS_Mean_F,
#               -TT_Sec_SSHG_Mean,
#               -TT_Sec_SSHG_Mean_F,
#               -TT_Sec_Med,
#               -TT_Sec_Med_F,
#               -TT_Sec_SS_Med,
#               -TT_Sec_SS_Med_F,
#               -TT_Sec_SSHG_Med,
#               -TT_Sec_SSHG_Med_F,
#               -TT_Sec_Cnt,
#               -TT_Sec_Cnt_F,
#               -TT_Sec_SS_Cnt,
#               -TT_Sec_SS_Cnt_F,
#               -TT_Sec_SSHG_Cnt,
#               -TT_Sec_SSHG_Cnt_F,
#               -TT_Hr_q2,
#               -TT_Hr_q98,
#               -TT_Hr_SS_q5,
#               -TT_Hr_SS_q95,
#               -TT_Hr_SSHG_q5,
#               -TT_Hr_SSHG_q95,
#               -TT_Hr_Mean,
#               -TT_Hr_Mean_F,
#               -TT_Hr_SS_Mean,
#               -TT_Hr_SS_Mean_F,
#               -TT_Hr_SSHG_Mean,
#               -TT_Hr_SSHG_Mean_F,
#               -TT_Hr_Med,
#               -TT_Hr_Med_F,
#               -TT_Hr_SS_Med,
#               -TT_Hr_SS_Med_F,
#               -TT_Hr_SSHG_Med,
#               -TT_Hr_SSHG_Med_F,
#               -TT_Hr_Cnt,
#               -TT_Hr_Cnt_F,
#               -TT_Hr_SS_Cnt,
#               -TT_Hr_SS_Cnt_F,
#               -TT_Hr_SSHG_Cnt,
#               -TT_Hr_SSHG_Cnt_F
#              ) %>% 
#        arrange(TravelDistance_Mi_NewHvrs) %>% 
#        head(500)
#     )

View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(TravelDistance_Mi_NewHvrs) %>%
       head(500)
    )

# examples of the smallest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1424440 & RowNum_OG <= 1424460) | # 1424450  --  direction change
                (RowNum_OG >= 763292 & RowNum_OG <= 763312) | # 763302  --  direction change
                (RowNum_OG >= 1679093 & RowNum_OG <= 1679113) | # 1679103  --  direction change
                (RowNum_OG >= 2860918 & RowNum_OG <= 2860938) # 2860928  --  looks correct
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )


View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(desc(TravelDistance_Mi_NewHvrs)
              ) %>%
       head(500)
    )

# examples of the largest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1092000 & RowNum_OG <= 1092050) | # 1092030  --  direction change
                (RowNum_OG >= 1609460 & RowNum_OG <= 1609480) | # 1609470  -- direction change 
                (RowNum_OG >= 508904 & RowNum_OG <= 508924) | # 508914  --  direction change & original StopID was bad
                (RowNum_OG >= 2476345 & RowNum_OG <= 2476365) # 2476355  --  direction change
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Investigation of TravelTime_Hr.

View(TravDistMi_Pctiles): 98% of TravelTime_Hr are between 7 seconds and 464 seconds (~8 minutes).


TravTimeHr_Ntile <- select(AllDays_NewTravelDist,
                           TravelTime_Hr
                          ) %>% 
  mutate(# Pctile = ntile(AllDays_NewTravelDist$TravelTime_Hr, 100),
         # MinR = min_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR = percent_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        ) 

# str(TravTimeHr_Ntile)

TravTimeHr_Ntile_Rows <- nrow(TravTimeHr_Ntile)

# View(tail(TravTimeHr_Ntile, 500))


TravTimeHr_Pctiles <- group_by(TravTimeHr_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTimeHr_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * 3600
        )

rm(TravTimeHr_Ntile_Rows)
rm(TravTimeHr_Ntile)
View(TravTimeHr_Pctiles)
TravTimeHr_Pctiles

Investigation of TravelTime_Hr.

Histogram of TravelTime_Sec.


TravTime_Sec_HistDen <- ggplot(filter(select(AllDays_NewTravelDist,
                                             TravelTime_Sec
                                            ),
                                      !is.na(TravelTime_Sec)
                                     ),
                               aes(x = TravelTime_Sec,
                                   y = ..density..
                                  )
                          ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  # stat_bin(binwidth = 5,
  #          geom = "text",
  #          size = 2.5,
  #          vjust = 1.5,
  #          aes(label = format(..count.., big.mark = ",")
  #             ),
  #         ) +
  coord_cartesian(xlim = c(0, 180), ylim = c(0, 0.02)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Time",
       x = "Travel Time (sec)",
       y = "Density"
      )

TravTime_Sec_HistDen

Investigation of TravelTime_Sec.

TravelTime_Sec values are NA.


summary(AllDays_NewTravelDist$TravelTime_Sec)


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(is.na(TravelTime_Sec) &
                BusDay_EventNum != 1  # TravelTime purposefully not calculated here
             )
    )

# examples of TravelTime_Sec values that are NA. These are NA because the Event_Time & Departure_Time readings are not accurate (i.e., the previous Departure_Time is BEFORE or EQUAL TO the current Event_Time).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 90809 & RowNum_OG <= 90829) | # 90819
                (RowNum_OG >= 90881 & RowNum_OG <= 90901) | # 90891
                (RowNum_OG >= 2597066 & RowNum_OG <= 2597086) | # 2597076
                (RowNum_OG >= 2613305 & RowNum_OG <= 2613325) # 2613315
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt"))
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely small.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(TravelTime_Sec,
               desc(SpeedAvg_Mph_NewHvrs)
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely large.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 1007703 & RowNum_OG <= 1007723) | # 1007713
                (RowNum_OG >= 2373564 & RowNum_OG <= 2373584) | # 2373574
                (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
                (RowNum_OG >= 2570060 & RowNum_OG <= 2570080) # 2570070
           )
    )

Investigation of TravelTime_Sec.

Are large TravelTime_Sec values related to RouteChanges? Looks likely. When the Bus involves a Route “change”, there is almost twice as likely to be a case of an outlier TravelTime_Sec value (on the high side).


TTLargeRteChng <- select(AllDays_NewTravelDist,
                         -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                        ) %>% 
  mutate(TT_Out = factor(ifelse(TravelTime_Sec > 464,  # this is the 99th percentile
                                "Outlier",
                                "Normal"
                               )
                        )
        )

# str(TTLargeRteChng)


TTLargeRteChng_Cnts <- group_by(TTLargeRteChng,
                                RteChange2,
                                TT_Out
                               ) %>% 
  summarise(Cnts = n()
           )

TTLargeRteChng_Spread <- as.data.frame(spread(TTLargeRteChng_Cnts,
                                              TT_Out,
                                              Cnts
                                             )
                                      ) %>%
  select(-RteChange2)

row.names(TTLargeRteChng_Spread) <- c("Change", "Same")
# str(TTLargeRteChng_Spread)


# When the Bus involves a Route "change", there is almost twice as likely to be a case of an outlier TravelTime_Sec value.
TTLargeRteChng_Spread
prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           2
          )

# rm(TTLargeRteChng, TTLargeRteChng_Spread)
         

View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

Investigation of TravelTime_Sec.

If TravelTime_Sec is below the 5th percentile for that StartStop_ID, or if TravelTime_Sec is above the 95th percentile for that StartStop_ID, consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TT_Sec_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


rm(TTLargeRteChng, TTLargeRteChng_Cnts, TTLargeRteChng_Spread)


NewTravTime <- mutate(AllDays_NewTravelDist,
                      TT_Sec_New = ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F >= 20,
                                          TT_Sec_SSHG_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt_F >= 20,
                                          TT_Sec_SS_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt >= 20,
                                          TT_Sec_SS_Mean,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt < 20 &
                                            RteChange2 == "Change",
                                          NA,
                                          TravelTime_Sec
                                         )))),
                      
                      TT_Sec_New_Label = 
           factor(ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F >= 20,
                         "TT_Sec_SSHG_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F < 20 &
                           TT_Sec_SS_Cnt_F >= 20,
                         "TT_Sec_SS_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                            ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt >= 20,
                         "TT_Sec_SS_Mean",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt < 20 &
                           RteChange2 == "Change",
                         NA,
                         "TravelTime_Sec"
                        ))))
                 ),
                  
                  TT_Hr_New = TT_Sec_New / (60 * 60)
           )


dim(AllDays_NewTravelDist)
dim(NewTravTime)
rm(AllDays_NewTravelDist)

summary(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )

str(select(NewTravTime,
           TravelTime_Sec,
           TT_Sec_New,
           TT_Sec_New_Label,
           TT_Hr_New
          )
   )


summary(select(NewTravTime,
               TravelTime_Sec,
               TT_Sec_New,
               TT_Sec_New_Label,
               TT_Hr_New
              )
       )

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by HourGroup).


View(head(select(NewTravTime,
                 -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                )
         )
    )

X2 <- select(NewTravTime,
             -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            ) %>% 
  filter(Route == "X2")

str(X2)

View(head(arrange(X2,
                  Bus_ID,
                  Event_Time
                 ),
          500
         )
    )

X2_ByStop <- group_by(X2,
                      StopID_Clean
                     ) %>% 
  arrange(StopID_Clean,
          Event_Time) %>% 
  mutate(Event_Time_L1 = lag(Event_Time),
         TimeToEvent_Sec = as.numeric(Event_Time - Event_Time_L1),
         TimeToEvent_Min = TimeToEvent_Sec / 60
        )

View(head(X2_ByStop, 500))


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(as.data.frame(X2_ByStop),
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                     )

TimeBtwEvents_X2_BoxPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                          TimeToEvent_Min,
                                          Event_Time_HrGroup
                                         ),
                                   aes(factor(Event_Time_HrGroup),
                                       TimeToEvent_Min,
                                       fill = factor(Event_Time_HrGroup)
                                      )
                                  ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 120)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Hour Group).


TimeBtwEvents_X2_ViolinPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                             TimeToEvent_Min,
                                             Event_Time_HrGroup
                                             ),
                                      aes(factor(Event_Time_HrGroup),
                                          TimeToEvent_Min,
                                          fill = factor(Event_Time_HrGroup)
                                         )
                                     ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 80)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by Zip Code).


# Count_Values is needed to display the medians on the box plots
Count_Values_z <- ddply(as.data.frame(X2_ByStop),
                        .(Stop_Zip),
                        summarise,
                        Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                       )

TimeBtwEvents_X2_BoxPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                            TimeToEvent_Min,
                                            Stop_Zip
                                           ),
                                     aes(factor(Stop_Zip),
                                         TimeToEvent_Min,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 100)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot_z

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).


TimeBtwEvents_X2_ViolinPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                               TimeToEvent_Min,
                                               Stop_Zip
                                               ),
                                        aes(factor(Stop_Zip),
                                            TimeToEvent_Min,
                                            fill = factor(Stop_Zip)
                                           )
                                       ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 60)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot_z

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

First, get the max and min times of bus stops (each day, and for each route).

RouteMinMax <- group_by(NewTravTime,
                        Route,
                        Event_Time_Date
                       ) %>% 
  summarise(MinTime = min(Event_Time),
            MaxTime = max(Event_Time)
           )
str(RouteMinMax)
Classes ‘grouped_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 1329 obs. of  4 variables:
 $ Route          : chr  "10A" "10A" "10A" "10A" ...
 $ Event_Time_Date: int  3 4 5 6 7 3 4 5 6 7 ...
 $ MinTime        : POSIXct, format: "2016-10-03 00:00:19" "2016-10-04 00:00:54" ...
 $ MaxTime        : POSIXct, format: "2016-10-03 23:57:27" "2016-10-04 23:59:23" ...
 - attr(*, "vars")=List of 1
  ..$ : symbol Route
 - attr(*, "drop")= logi TRUE
View(RouteMinMax)

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by day, as the data are too large to do at once.)

# Bind all the individual dataframes together.
WaitData_DayPull <- bind_rows(Testing_3,
                              Testing_4,
                              Testing_5,
                              Testing_6,
                              Testing_7
                             ) %>% 
  mutate(WaitTime_Sec3 = NB - SampTime,
         WaitTime_Min3 = WaitTime_Sec3 / 60
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )
str(WaitData_DayPull)
'data.frame':   2666526 obs. of  23 variables:
 $ RowNum_OG         : int  771269 510393 842137 416282 403679 478483 842251 403790 842364 403906 ...
 $ Route             : chr  "10A" "10A" "10A" "10A" ...
 $ Event_Time_Date   : int  3 3 3 3 3 3 3 3 3 3 ...
 $ StopID_Clean      : chr  "2" "2" "2" "2" ...
 $ Event_Type        : int  3 4 3 3 4 3 3 4 3 4 ...
 $ Event_Description : Factor w/ 3 levels "Serviced Stop                                     ",..: 1 3 1 1 3 1 1 3 1 3 ...
 $ Event_Time_Yr     : int  2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
 $ Event_Time_Mth    : int  10 10 10 10 10 10 10 10 10 10 ...
 $ Event_Time_Day    : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tues"<..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Event_Time_Hr     : int  0 1 5 5 6 6 7 8 9 10 ...
 $ Event_Time_HrGroup: Ord.factor w/ 8 levels "Group0_2"<"Group3_5"<..: 1 1 2 2 3 3 3 3 4 4 ...
 $ Event_Time_Min    : int  1 3 5 38 21 40 12 36 21 27 ...
 $ Event_Time        : POSIXct, format: "2016-10-03 00:01:53" "2016-10-03 01:03:51" ...
 $ MinTime           : POSIXct, format: "2016-10-03 00:00:19" "2016-10-03 00:00:19" ...
 $ MaxTime           : POSIXct, format: "2016-10-03 23:57:27" "2016-10-03 23:57:27" ...
 $ SampTime          : POSIXct, format: "2016-10-03 15:35:56" "2016-10-03 10:41:52" ...
 $ NB                : POSIXct, format: "2016-10-03 16:01:44" "2016-10-03 10:42:48" ...
 $ WaitTime_Min      : num  25.79 55.82 2.44 28.76 1.48 ...
 $ WaitTime_Sec      : num  1547.1 3349.2 146.6 1725.5 88.9 ...
 $ WaitTime_Sec2     : num  25.79 55.82 2.44 28.76 1.48 ...
 $ WaitTime_Min2     : num  0.4298 0.9303 0.0407 0.4793 0.0247 ...
 $ WaitTime_Sec3     :Class 'difftime'  atomic [1:2666526] 1547.1 55.8 8794.3 1725.5 5333.4 ...
  .. ..- attr(*, "units")= chr "secs"
 $ WaitTime_Min3     :Class 'difftime'  atomic [1:2666526] 25.79 0.93 146.57 28.76 88.89 ...
  .. ..- attr(*, "units")= chr "secs"
View(head(WaitData_DayPull, 500))
View(tail(WaitData_DayPull, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Basic investigation of any missing rows from data pulled by day.


DistinctRowNum_OG <- distinct(select(WaitData_DayPull,
                                     RowNum_OG
                                    )
                             )

str(DistinctRowNum_OG)

View(
anti_join(Samp,
          DistinctRowNum_OG,
          by = c("RowNum_OG" = "RowNum_OG")
         )
)


# The samp time is AFTER the last bus passed that StopID_Clean
View(filter(Samp,
            Event_Time > "2016-10-07 19:48:41" &
              Route == "X2" &
              StopID_Clean == 1003774
           )
    )

# Next Bus (NB) can be on the next morning
View(filter(Testing7,
            SampTime > "2016-10-06 23:58:00" &
              SampTime < "2016-10-06 23:59:59")
    )

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)

First, we need to find the most common bus routes.

rm(DistinctRowNum_OG)
object 'DistinctRowNum_OG' not found
# View(head(NewTravTime, 500))
set.seed(123456789)
BusGroups <- group_by(NewTravTime,
                      Route
                     ) %>% 
  summarise(Cnt_Num = n(),
            Cnt_Pct = Cnt_Num / nrow(NewTravTime)
           ) %>% 
  arrange(desc(Cnt_Num)
         ) %>% 
  mutate(RowNum = row_number(),
         RandNum = runif(n = 268),
         RouteGroup = ifelse(RandNum <= 0.2,
                             1,
                      ifelse(RandNum <= 0.4,
                             2,
                      ifelse(RandNum <= 0.6,
                             3,
                      ifelse(RandNum <= 0.8,
                             4,
                             5
                            ))))
        )
str(BusGroups)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   268 obs. of  6 variables:
 $ Route     : chr  "70" "W4" "B2" "S2" ...
 $ Cnt_Num   : int  48269 47672 43173 42934 41462 38968 38566 37761 37718 36524 ...
 $ Cnt_Pct   : num  0.0172 0.017 0.0154 0.0153 0.0148 ...
 $ RowNum    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ RandNum   : num  0.693 0.673 0.654 0.719 0.922 ...
 $ RouteGroup: num  4 4 4 4 5 5 2 5 2 3 ...
View(BusGroups)
summary(BusGroups)
    Route              Cnt_Num         Cnt_Pct              RowNum      
 Length:268         Min.   :    4   Min.   :1.424e-06   Min.   :  1.00  
 Class :character   1st Qu.: 2640   1st Qu.:9.396e-04   1st Qu.: 67.75  
 Mode  :character   Median : 7358   Median :2.619e-03   Median :134.50  
                    Mean   :10483   Mean   :3.731e-03   Mean   :134.50  
                    3rd Qu.:17014   3rd Qu.:6.056e-03   3rd Qu.:201.25  
                    Max.   :48269   Max.   :1.718e-02   Max.   :268.00  
    RandNum           RouteGroup   
 Min.   :0.001084   Min.   :1.000  
 1st Qu.:0.255701   1st Qu.:2.000  
 Median :0.512479   Median :3.000  
 Mean   :0.501473   Mean   :3.022  
 3rd Qu.:0.756575   3rd Qu.:4.000  
 Max.   :0.997351   Max.   :5.000  

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)

str(WaitData_RoutePull)
'data.frame':   2780848 obs. of  22 variables:
 $ RowNum_OG         : int  771269 510393 842137 416282 403679 478483 842251 403790 842364 403906 ...
 $ Route             : chr  "10A" "10A" "10A" "10A" ...
 $ RouteGroup        : num  4 4 4 4 4 4 4 4 4 4 ...
 $ Event_Time_Date   : int  3 3 3 3 3 3 3 3 3 3 ...
 $ StopID_Clean      : chr  "2" "2" "2" "2" ...
 $ Event_Type        : int  3 4 3 3 4 3 3 4 3 4 ...
 $ Event_Description : Factor w/ 3 levels "Serviced Stop                                     ",..: 1 3 1 1 3 1 1 3 1 3 ...
 $ Event_Time_Yr     : int  2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
 $ Event_Time_Mth    : int  10 10 10 10 10 10 10 10 10 10 ...
 $ Event_Time_Day    : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tues"<..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Event_Time_Hr     : int  0 1 5 5 6 6 7 8 9 10 ...
 $ Event_Time_HrGroup: Ord.factor w/ 8 levels "Group0_2"<"Group3_5"<..: 1 1 2 2 3 3 3 3 4 4 ...
 $ Event_Time_Min    : int  1 3 5 38 21 40 12 36 21 27 ...
 $ Event_Time        : POSIXct, format: "2016-10-03 00:01:53" "2016-10-03 01:03:51" ...
 $ MinTime           : POSIXct, format: "2016-10-03 00:00:19" "2016-10-03 00:00:19" ...
 $ MaxTime           : POSIXct, format: "2016-10-03 23:57:27" "2016-10-03 23:57:27" ...
 $ SampTime          : POSIXct, format: "2016-10-03 08:00:38" "2016-10-03 02:41:02" ...
 $ NB                : POSIXct, format: "2016-10-03 08:36:07" "2016-10-03 05:05:41" ...
 $ WaitTime_Min      : num  35.47 2.41 22.98 4.94 7.99 ...
 $ WaitTime_Sec      : num  2128 145 1379 296 480 ...
 $ WaitTime_Sec2     :Class 'difftime'  atomic [1:2780848] 2128 8679 1379 296 480 ...
  .. ..- attr(*, "units")= chr "secs"
 $ WaitTime_Min2     :Class 'difftime'  atomic [1:2780848] 35.47 144.65 22.98 4.94 7.99 ...
  .. ..- attr(*, "units")= chr "secs"

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData pulled by day and pulled by route.

WaitData_Diff <- anti_join(WaitData_RoutePull,
                           WaitData_DayPull,
                           by = c("RowNum_OG" = "RowNum_OG"
                                 )
                          ) %>% 
  select(-WaitTime_Min,
         -WaitTime_Sec
        )
str(WaitData_Diff)
'data.frame':   130807 obs. of  20 variables:
 $ RowNum_OG         : int  2902760 2952760 2637547 1771590 2911289 1129658 1780069 1729217 1273777 2950017 ...
 $ Route             : chr  "Z8" "Z8" "Z8" "Z8" ...
 $ RouteGroup        : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Event_Time_Date   : int  7 7 6 5 7 6 6 6 3 7 ...
 $ StopID_Clean      : chr  "2005465" "2005465" "2005465" "2005465" ...
 $ Event_Type        : int  3 3 4 3 3 4 3 4 4 3 ...
 $ Event_Description : Factor w/ 3 levels "Serviced Stop                                     ",..: 1 1 3 1 1 3 1 3 3 1 ...
 $ Event_Time_Yr     : int  2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
 $ Event_Time_Mth    : int  10 10 10 10 10 10 10 10 10 10 ...
 $ Event_Time_Day    : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tues"<..: 6 6 5 4 6 5 5 5 2 6 ...
 $ Event_Time_Hr     : int  19 10 15 17 17 21 17 8 8 18 ...
 $ Event_Time_HrGroup: Ord.factor w/ 8 levels "Group0_2"<"Group3_5"<..: 7 4 6 6 6 8 6 3 3 7 ...
 $ Event_Time_Min    : int  51 18 49 55 40 8 7 17 10 7 ...
 $ Event_Time        : POSIXct, format: "2016-10-07 19:51:47" "2016-10-07 10:18:57" ...
 $ MinTime           : POSIXct, format: "2016-10-07 00:00:07" "2016-10-07 00:00:07" ...
 $ MaxTime           : POSIXct, format: "2016-10-07 23:59:55" "2016-10-07 23:59:55" ...
 $ SampTime          : POSIXct, format: "2016-10-07 04:55:14" "2016-10-07 21:41:22" ...
 $ NB                : POSIXct, format: "2016-10-07 05:12:42" "2016-10-07 21:45:51" ...
 $ WaitTime_Sec2     :Class 'difftime'  atomic [1:130807] 1048 269 10822 465 1354 ...
  .. ..- attr(*, "units")= chr "secs"
 $ WaitTime_Min2     :Class 'difftime'  atomic [1:130807] 17.46 4.48 180.36 7.75 22.56 ...
  .. ..- attr(*, "units")= chr "secs"
View(head(WaitData_Diff, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData (pulled by route) and original data (NewTravTime).

filter(Samp,
              Route == "X2" &
              StopID_Clean == 1003774
            # RowNum_OG = 1146723
            # Event_Time = 2016-10-07 15:32:18
           )
Error in filter_(.data, .dots = lazyeval::lazy_dots(...)) : 
  object 'Samp' not found

Clean up the data a bit.

rm(Compare_NTT_WD)
str(WaitTime_AsNum)
'data.frame':   2810109 obs. of  64 variables:
 $ RowNum_OG                      : int  771269 510393 842137 416282 403679 478483 842251 403790 842364 403906 ...
 $ UniqueLatLng                   : chr  "38.867313__-77.053574" "38.867313__-77.053574" "38.867313__-77.053574" "38.867313__-77.053574" ...
 $ group                          : Factor w/ 5 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ StartStop_ID                   : chr  "6000273--2" "6000273--2" "6000273--2" "6000273--2" ...
 $ BusDay_EventNum                : int  2 70 55 55 55 94 164 158 272 266 ...
 $ Bus_ID                         : int  2915 2719 2950 2634 2625 2674 2950 2625 2950 2625 ...
 $ Route                          : chr  "10A" "10A" "10A" "10A" ...
 $ RteChange2                     : Factor w/ 2 levels "Change","Same": 2 2 2 2 2 2 2 2 2 2 ...
 $ RouteAlt                       : Factor w/ 14 levels "1","10","11",..: 6 6 6 6 6 6 6 6 6 6 ...
 $ DirChange2                     : Factor w/ 2 levels "Change","Same": 2 2 2 2 2 2 2 2 2 2 ...
 $ Route_Direction                : Factor w/ 12 levels "","ANTICLKW",..: 7 7 7 7 7 7 7 7 7 7 ...
 $ Stop_Sequence                  : int  55 55 55 55 55 55 55 55 55 55 ...
 $ Start_ID                       : chr  "6000273" "6000273" "6000273" "6000273" ...
 $ Start_Desc                     : chr  "ARMY-NAVY DR + S HAYES ST" "ARMY-NAVY DR + S HAYES ST" "ARMY-NAVY DR + S HAYES ST" "ARMY-NAVY DR + S HAYES ST" ...
 $ StopID_Clean                   : chr  "2" "2" "2" "2" ...
 $ StopID_Indicator               : Factor w/ 2 levels "ID_Bad","ID_OK": 1 1 1 1 1 1 1 1 1 1 ...
 $ Stop_Desc                      : chr  "PENTAGON INBOUND STOP" "PENTAGON INBOUND STOP" "PENTAGON INBOUND STOP" "PENTAGON INBOUND STOP" ...
 $ countryCode                    : Factor w/ 1 level "US": 1 1 1 1 1 1 1 1 1 1 ...
 $ Stop_State                     : Factor w/ 3 levels "DC","MD","VA": 3 3 3 3 3 3 3 3 3 3 ...
 $ Stop_County                    : Factor w/ 11 levels "Anne Arundel",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Stop_City                      : Factor w/ 56 levels "Accokeek","Alexandria",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ Stop_Zip                       : Factor w/ 153 levels "20001","20002",..: 132 132 132 132 132 132 132 132 132 132 ...
 $ Event_Type                     : int  3 4 3 3 4 3 3 4 3 4 ...
 $ Event_Description              : Factor w/ 3 levels "Serviced Stop                                     ",..: 1 3 1 1 3 1 1 3 1 3 ...
 $ Event_Time_Yr                  : int  2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
 $ Event_Time_Mth                 : int  10 10 10 10 10 10 10 10 10 10 ...
 $ Event_Time_Date                : int  3 3 3 3 3 3 3 3 3 3 ...
 $ Event_Time_Day                 : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tues"<..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Event_Time_Hr                  : int  0 1 5 5 6 6 7 8 9 10 ...
 $ Event_Time_HrGroup             : Ord.factor w/ 8 levels "Group0_2"<"Group3_5"<..: 1 1 2 2 3 3 3 3 4 4 ...
 $ Event_Time_Min                 : int  1 3 5 38 21 40 12 36 21 27 ...
 $ Event_Time                     : POSIXct, format: "2016-10-03 00:01:53" "2016-10-03 01:03:51" ...
 $ Departure_Time                 : POSIXct, format: "2016-10-03 00:01:53" "2016-10-03 01:03:51" ...
 $ Dwell_Time                     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Dwell_Time2                    : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Delta_Time                     : int  -210 -89 -35 149 914 253 217 1267 400 900 ...
 $ Latitude                       : num  38.9 38.9 38.9 38.9 38.9 ...
 $ Longitude                      : num  -77.1 -77.1 -77.1 -77.1 -77.1 ...
 $ Heading                        : int  23 23 23 23 23 23 23 23 23 23 ...
 $ Odometer_Distance              : int  1131407 909311 87585 80914 88439 69784 211146 212739 336615 337781 ...
 $ Odometer_Distance_Lag1         : int  1131407 908412 87585 80914 85325 69784 211146 211995 336615 337065 ...
 $ Odometer_Distance_Mi           : num  214.3 172.2 16.6 15.3 16.7 ...
 $ TravelDistance_Ft              : int  NA 899 NA NA 3114 NA NA 744 NA 716 ...
 $ TravelDistance_Mi              : num  NA 0.17 NA NA 0.59 ...
 $ TravelDistance_Mi_Hvrs         : num  0.322 0.322 0.322 0.322 0.319 ...
 $ TravelTime_Sec                 : num  94 191 130 85 183 114 183 183 124 128 ...
 $ TravelTime_Hr                  : num  0.0261 0.0531 0.0361 0.0236 0.0508 ...
 $ SpeedAvg_Mph                   : num  NA 3.21 NA NA 11.6 ...
 $ TravelDistance_Mi_New          : num  0.322 0.17 0.322 0.322 0.59 ...
 $ TravelDistance_Mi_New_Label    : Factor w/ 5 levels "TD_Mi_SS_Mean",..: 5 4 5 5 4 5 5 4 5 4 ...
 $ TravelDistance_Mi_NewHvrs      : num  0.322 0.17 0.322 0.322 0.59 ...
 $ TravelDistance_Mi_NewHvrs_Label: Factor w/ 5 levels "TD_Mi_SS_Mean",..: 5 4 5 5 4 5 5 4 5 4 ...
 $ SpeedAvg_Mph_NewHvrs           : num  12.32 3.21 8.91 13.62 11.6 ...
 $ TT_Sec_New                     : num  94 191 130 85 183 114 183 183 124 128 ...
 $ TT_Sec_New_Label               : Factor w/ 4 levels "TravelTime_Sec",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ TT_Hr_New                      : num  0.0261 0.0531 0.0361 0.0236 0.0508 ...
 $ RouteGroup                     : num  4 4 4 4 4 4 4 4 4 4 ...
 $ MinTime                        : POSIXct, format: "2016-10-03 00:00:19" "2016-10-03 00:00:19" ...
 $ MaxTime                        : POSIXct, format: "2016-10-03 23:57:27" "2016-10-03 23:57:27" ...
 $ SampTime                       : POSIXct, format: "2016-10-03 08:00:38" "2016-10-03 02:41:02" ...
 $ NB                             : POSIXct, format: "2016-10-03 08:36:07" "2016-10-03 05:05:41" ...
 $ WaitTime_Sec2                  : num  2128 8679 1379 296 480 ...
 $ WaitTime_Min2                  : num  35.47 144.65 22.98 4.94 7.99 ...
 $ RouteStop_ID                   : Factor w/ 20897 levels "10A__2","10A__3",..: 1 1 1 1 1 1 1 1 1 1 ...

General exploration of wait times.

filter(WaitTime_AsNum,
            Route == "D8" &  # route has VERY limited service after midnight
              StopID_Clean == 1001669
            # Event_Time = 2016-10-06 20:31:16
           )
Error in filter_(.data, .dots = lazyeval::lazy_dots(...)) : 
  object 'WaitTime_AsNum' not found

Looks like there might be an issue in wait times when very few Route-Stop combinations are included in the dataset. Let’s explore these.

Histogram of the counts of Route-StopID combinations.

RouteStop_Cnts_Bar <- ggplot(RouteStop_CntOfCnt,
                             aes(x = RouteStop_CntNum,
                                 # y = ..density..
                                 y = RouteStopCnt_CntNum
                                )
                            ) +
  # geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  coord_cartesian(xlim = c(0, 500)
                  # ylim = c(0, 0.02)
                 ) +
  labs(title = "Variation in Routes Passing a Specific Stop",
       x = "Occurrences of Route-StopID Combiantions",
       y = "Counts"
      )
RouteStop_Cnts_Bar

Create a new dataset limiting extremely small counts of Route-StopID combinations.

filter(WaitTime_RteCnts,
       RouteStop_CntNum > 60  # 12 passes per day in a 5-day dataset
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()
 WaitTime_Min2     
 Min.   :   0.000  
 1st Qu.:   7.477  
 Median :  16.512  
 Mean   :  49.070  
 3rd Qu.:  33.572  
 Max.   :2478.582  
 NA's   :16298     
filter(WaitTime_RteCnts,
       WaitTime_Min2 < 180  # probably means that something went wrong
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()
 WaitTime_Min2    
 Min.   :  0.000  
 1st Qu.:  6.969  
 Median : 15.156  
 Mean   : 25.022  
 3rd Qu.: 28.187  
 Max.   :180.000  

Histogram of all wait times.

WaitTime_AllBus_HistDen <- ggplot(filter(select(WaitTime_RteCnts,
                                                WaitTime_Min2
                                               ),
                                         !is.na(WaitTime_Min2)
                                        ),
                                  aes(x = WaitTime_Min2,
                                      y = ..density..
                                     )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  scale_x_continuous(breaks = seq(0, 300, 30)
                    ) +
  coord_cartesian(xlim = c(0, 300),
                  ylim = c(0, 0.035)
                 ) +
  labs(title = "Variation in Wait Time",
       x = "Wait Time (min)",
       y = "Density"
      )
WaitTime_AllBus_HistDen

Box plots for WaitTime (all busses, by Zip Code).

# Count_Values is needed to display the medians on the box plots
BusRoute <- select(WaitTime_RteCnts,
                   Route,
                   WaitTime_Min2,
                   Stop_Zip
                  ) %>% 
  filter(Route == "X2")
CountValues_AllBus_Zip <- ddply(BusRoute,
                                .(Stop_Zip),
                                summarise,
                                Value_Counts = median(WaitTime_Min2, na.rm = TRUE)
                               )
WaitTime_AllBus_Zip_Box <- ggplot(BusRoute,
                                  aes(factor(Stop_Zip),
                                      WaitTime_Min2,
                                      fill = factor(Stop_Zip)
                                     )
                                 ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )
WaitTime_AllBus_Zip_Box

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).

WaitTime_AllBus_Zip_Violin <- ggplot(BusRoute,
                                     aes(factor(Stop_Zip),
                                         WaitTime_Min2,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )
TimeBtwEvents_X2_ViolinPlot_z

Box plots for WaitTime (Zip Code, by HourGroupZip).

# Count_Values is needed to display the medians on the box plots
Zip <- select(WaitTime_RteCnts,
              Route,
              WaitTime_Min2,
              Stop_Zip,
              Event_Time_HrGroup
             ) %>% 
  filter(Stop_Zip == 20002)
CountValues_AllBus_HG <- ddply(Zip,
                               .(Event_Time_HrGroup),
                               summarise,
                               Value_Counts = median(WaitTime_Min2,
                                                     na.rm = TRUE
                                                    )
                               )
WaitTime_AllBus_HG_Box <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )
WaitTime_AllBus_HG_Box

Violin plots for WaitTime (Zip Code, by HourGroupZip).

WaitTime_AllBus_HG_Vln <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 90)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )
WaitTime_AllBus_HG_Vln

Box plots for WaitTime (Route, by HourGroupZip).

Violin plots for WaitTime (Zip Code, by HourGroupZip).

X2 Percentiles Line Graph Test.

GET DATA READY FOR SHINY

Shiny_WaitData_Base <- select(WaitTime_RteCnts,
                              Route,
                              Stop_Zip,
                              Event_Time_Date,
                              Event_Time_Day,
                              Event_Time_HrGroup,
                              Event_Time_Hr,
                              Latitude,
                              Longitude,
                              WaitTime_Min2
                             )
Shiny_WaitData_Base$Route <- factor(Shiny_WaitData_Base$Route)
str(Shiny_WaitData_Base)
'data.frame':   2810109 obs. of  9 variables:
 $ Route             : Factor w/ 268 levels "10A","10B","10E",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Stop_Zip          : Factor w/ 153 levels "20001","20002",..: 132 132 132 132 132 132 132 132 132 132 ...
 $ Event_Time_Date   : int  3 3 3 3 3 3 3 3 3 3 ...
 $ Event_Time_Day    : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tues"<..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Event_Time_HrGroup: Ord.factor w/ 8 levels "Group0_2"<"Group3_5"<..: 1 1 2 2 3 3 3 3 4 4 ...
 $ Event_Time_Hr     : int  0 1 5 5 6 6 7 8 9 10 ...
 $ Latitude          : num  38.9 38.9 38.9 38.9 38.9 ...
 $ Longitude         : num  -77.1 -77.1 -77.1 -77.1 -77.1 ...
 $ WaitTime_Min2     : num  35.47 144.65 22.98 4.94 7.99 ...
write.table(Shiny_WaitData_Base,
            "Shiny_WaitData_Base.txt",
            quote = FALSE,
            sep = "\t",
            row.names = FALSE
           )

Testing mapping functionaltiy

map <- get_map(location = c(lon = -77.03676, lat = 38.89784),
               source = "google",
               # maptype = "roadmap"
               zoom = 12
              )
Source : https://maps.googleapis.com/maps/api/staticmap?center=38.89784,-77.03676&zoom=12&size=640x640&scale=2&maptype=terrain&language=en-EN
ggmap(map) +
  geom_polygon(aes(x = long, y = lat, group = group), 
               data = ggtract,
               colour = 'white', 
               fill = 'black', 
               alpha = .4, 
               size = .3
              )

NA

Investigating TravelTime_Sec.


View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(NewTravTime,
            # -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            -(TD_Mi_q2:TD_Mi_SSHG_Cnt_F),
            -(TT_Hr_q2:TT_Hr_SSHG_Cnt_F)
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )






# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

         
         
# examples where TravelTime_Sec is unusually small (with TravelDistance_Mi values that are large).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1042228 & RowNum_OG <= 1042248) | # 1042238
                (RowNum_OG >= 53816 & RowNum_OG <= 53836) | # 53826
                (RowNum_OG >= 360571 & RowNum_OG <= 360591) | # 360581
                (RowNum_OG >= 502271 & RowNum_OG <= 502291) # 502281 (can't explian the weird TravelTime_Sec calculation here - it's not even an integer!)
           )
    )

# still trying to explain 502281...on the day of this weirdness, the bus was only in circulation for 4-5 stops (~20 minutes) on that day (Oct 6)
View(filter(AllDays_NewTravelDist,
            Bus_ID == 2711
           )
    )


# exploring large values for TravelTime_Sec
View(filter(AllDays_NewTravelDist,
            TravelTime_Sec == 300
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph2
              )
    )

# examples where TravelTime_Sec is unusually large (with TravelDistance_Mi values that are small, so SpeedAvg_Mph values are very small).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 2627459 & RowNum_OG <= 2627479) | # 2627469
                (RowNum_OG >= 2193344 & RowNum_OG <= 2193364) | # 2193354
                (RowNum_OG >= 1644123 & RowNum_OG <= 1644143) | # 1644133
                (RowNum_OG >= 869600 & RowNum_OG <= 869620) # 869610
           )
    )

Investigation of SpeedAvg_Mph2

View(Speed_Pctiles): 90% of SpeedAvg_Mph2 are between ~3mph and ~66mph.


Speed_Ntile <- as.data.frame(AllDays_NewTravelDist$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(AllDays_NewTravelDist$SpeedAvg_Mph2, 100),
         MinR = min_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR = percent_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_Ntile)

Speed_Ntile_Rows <- nrow(Speed_Ntile)

View(tail(Speed_Ntile, 500))


Speed_Pctiles <- group_by(Speed_Ntile,
                          PctR_Round
                         ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_Pctiles)

Investigation of SpeedAvg_Mph2.

Exploring the removal of outlier TravelTime_Sec and TravelDistance_Mi.


summary(select(AllDays_NewTravelDist,
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      TravelDistance_Mi > 0.0001893939 & # lowest non-zero percentile
                        TravelDistance_Mi < 1.0812500000 & # 99th percentile
                        TravelTime_Sec > 10.050000 & # 2nd percentile
                        TravelTime_Sec < 293.000000 # 98th percentile
                     ),
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2.


Speed_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                               !is.na(SpeedAvg_Mph2)
                              ),
                        aes(x = SpeedAvg_Mph2,
                            y = ..density..
                           )
                       ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       x = "Average Speed (mph)",
       y = "Density"
      )

Speed_HistDen

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2 after removing outlier TravelTime_Sec and TravelDistance_Mi.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

SpeedNoOutlier_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                                        !is.na(SpeedAvg_Mph2) &
                                          TravelDistance_Mi_New > 0.077841005 & # 5th percentile
                                          # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                          TravelTime_Sec > 12.100000 # 4th percentile
                                          # TravelTime_Sec < 293.000000 # 98th percentile
                                       ),
                                 aes(x = SpeedAvg_Mph2,
                                     y = ..density..
                                    )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       subtitle = "(removed low outliers of Travel Distance and Travel Time)",
       x = "Average Speed (mph)",
       y = "Density"
      )

SpeedNoOutlier_HistDen

Investigation of SpeedAvg_Mph2.

New dataset (NoOutliers_TravelDistNTime) when removing outlier low values of TravelDistance_Mi_New and TravelTime_Sec.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

NoOutliers_TravelDistNTime <- filter(AllDays_NewTravelDist,
                                     TravelDistance_Mi_New > .077841005 & # 5th percentile
                                       # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                       TravelTime_Sec > 12.100000 # 4th percentile
                                       # TravelTime_Sec < 293.000000 # 98th percentile
                                    )

nrow(AllDays_NewTravelDist) - nrow(NoOutliers_TravelDistNTime)

str(NoOutliers_TravelDistNTime)
summary(NoOutliers_TravelDistNTime)

Investigation of SppedAvg_Mph2.

View(Speed_NoOut_Pctiles): Aproximately 90% of SpeedAvg_Mph2 values are between ~4mph and ~56mph.


Speed_NoOut_Ntile <- as.data.frame(NoOutliers_TravelDistNTime$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(NoOutliers_TravelDistNTime$SpeedAvg_Mph2, 100),
         MinR = min_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR = percent_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_NoOut_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_NoOut_Ntile)

Speed_NoOut_Ntile_Rows <- nrow(Speed_NoOut_Ntile)

View(tail(Speed_NoOut_Ntile, 500))


Speed_NoOut_Pctiles <- group_by(Speed_NoOut_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_NoOut_Pctiles)

Investigation of SppedAvg_Mph2.

Exloring odd/impossible values.


# Exploring when SpeedAvg_Mph2 is NA  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            is.na(SpeedAvg_Mph2)
           )
    )


# Exploring when SpeedAvg_Mph2 is zero  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            SpeedAvg_Mph2 == 0
           )
    )


# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            SpeedAvg_Mph2 > 0 &
              SpeedAvg_Mph2 < 3.2848770
           ) %>% 
       arrange(SpeedAvg_Mph2)
    )

# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 485338 & RowNum_OG <= 485358) | # 485348  --  Extreme travel time, Route Change
                (RowNum_OG >= 346952 & RowNum_OG <= 346972) | # 346962  -- Extreme travel time, Route Change 
                (RowNum_OG >= 70494 & RowNum_OG <= 70514) | # 70504  --  Extreme travel time, Route Change
                (RowNum_OG >= 2051846 & RowNum_OG <= 2051866) # 2051856  --  Extreme travel time, Route Change
           )
    )

Investigation of SpeedAvg_Mph2.

Limit the dataset based on SpeedAvg_Mph2.


NoOutliersSpeed <- filter(NoOutliers_TravelDistNTime,
                          between(SpeedAvg_Mph2,
                                  4.069300, # 5th percentile
                                  56.05651 #95th percentile
                                 )
                          )

nrow(NoOutliers_TravelDistNTime) - nrow(NoOutliersSpeed)

summary(NoOutliersSpeed)

TravelTime now looks like it has some odd values on the high end. So let’s look at those.

View(TravTime_NoOut_Pctiles): Virtually all trips should take less than 5 minutes. (The 99th percentile of of TravelTime is approximately 8 minutes.)


TravTime_NoOut_Ntile <- as.data.frame(NoOutliersSpeed$TravelTime_Hr) %>% 
  mutate(Pctile = ntile(NoOutliersSpeed$TravelTime_Hr, 100),
         MinR = min_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR = percent_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        )

colnames(TravTime_NoOut_Ntile)[1] <- "TravelTime_Hr"
str(TravTime_NoOut_Ntile)

TravTime_NoOut_Ntile_Rows <- nrow(TravTime_NoOut_Ntile)

View(tail(TravTime_NoOut_Ntile, 500))


TravTime_NoOut_Pctiles <- group_by(TravTime_NoOut_Ntile,
                                   PctR_Round
                                  ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTime_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * (60 * 60)
        )

View(TravTime_NoOut_Pctiles)

Investigating odd TravelTime_Sec values.

Trips longer than ~8 minutes.


View(filter(NoOutliersSpeed,
            TravelTime_Sec > 491 # min at the 100th percentile
           ) %>% 
       arrange(desc(TravelTime_Sec)
              )
    )

# examples of TravelTime_Sec values that are largest.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 2071759 & RowNum_OG <= 2071779) | # 2071769  --  results from a route change, and a 3hr+ wait before the new route starts
                (RowNum_OG >= 1473686 & RowNum_OG <= 1473706) | # 1473696  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 1222822 & RowNum_OG <= 1222842) | # 1222832  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 3046089 & RowNum_OG <= 3046109) # 3046099  --  results from a route change, and a 3hr wait before the new route starts
           )
    )


# examples of TravelTime_Sec values that are the smallest of the large.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 3044689 & RowNum_OG <= 3044709) | # 3044699  --  results from a route change
                (RowNum_OG >= 3022358 & RowNum_OG <= 3022378) | # 3022368  --  results from a route change
                (RowNum_OG >= 2993016 & RowNum_OG <= 2993036) | # 2993026  --  results from a previous route change (change occurred in deleted row)
                (RowNum_OG >= 2683703 & RowNum_OG <= 2683723) # 2683713  --  results from a previous route change (change occurred in deleted row)
           )
    )

Let’s look at the TravelTime_Sec values and route changes (DirChange2).

The 99th percentile of TravelTime_Sec for both, all trips, and just those trips NOT involving route changes (DirChange2 = “Same”), is approximately 5min (300 sec).

Nota Bene: The percentile calculation here is defined slightly different than in most of the above analyses (which get the lowest value in the bin created by 100 ntiles).


summary(select(NoOutliersSpeed,
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Same"
                     ),
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Change"
                     ),
               TravelTime_Sec
              )
       )


TravTimeSec_Qtiles_df <- data.frame(PctValue = seq(0, 100, 1),
                                    All = seq(1, 101, 1),
                                    Same = seq(1, 101, 1),
                                    Change = seq(1, 101, 1)
                                   )

TravTimeSec_Qtiles_df[ , 2] <- quantile(select(NoOutliersSpeed,
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 3] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Same"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 4] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Change"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

View(TravTimeSec_Qtiles_df)

Limit the dataset now based on TravelTime_Sec.


UpperLimitTravTime <- filter(NoOutliersSpeed,
                             TravelTime_Sec <= 491 # min at the 100th percentile
                             )

nrow(NoOutliersSpeed) - nrow(UpperLimitTravTime)

str(UpperLimitTravTime)

summary(UpperLimitTravTime)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Differences between Dwell_Time (by WMATA) and Dwell_Time2 (by me) appear to be due to switches in RouteAlt. WMATA calculates Dwell_Time by an unknown process. The WMATA calculation is equal to my calculation, except for the records immedaitely before and after a RouteAlt switch (DirChange2).


View(filter(AllDays_NewOrder,
            Dwell_Time != Dwell_Time2
           )
    )


# Examples where the Dwell_Time and Dwell_Time2 are different
View(filter(AllDays_NewOrder,
            ( (RowNum_OG >= 65 & RowNum_OG <= 85) | # 75
                (RowNum_OG >= 162 & RowNum_OG <= 192) | # 172
                (RowNum_OG >= 431952 & RowNum_OG <= 431972) | # 431962
                (RowNum_OG >= 434595 & RowNum_OG <= 434615) # 434605  --  this record is NOT a route switch, but does has a Sequence switch (Me: should there really be a route switch here?)
            )
           )
    )

Investigation of Dwell_Time2 (how long the bus is at a stop).

First, create some “rank” stats. View(DT2_Pctiles): 95% of Dwell_Time2s are <= 23 seconds…but some weird (e.g., nearly 2 hour Dwell_Time2s exist).


DwellTime2_Ntile <- as.data.frame(AllDays_NewOrder$Dwell_Time2) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Dwell_Time2, 100),
         MinR = min_rank(AllDays_NewOrder$Dwell_Time2),
         PctR = percent_rank(AllDays_NewOrder$Dwell_Time2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DwellTime2_Ntile)[1] <- "Dwell_Time2"
str(DwellTime2_Ntile)

DwellTime2_Ntile_Rows <- nrow(DwellTime2_Ntile)

View(tail(DwellTime2_Ntile, 500))


DwellTime2_Pctiles <- group_by(DwellTime2_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinDwellAtPctile = min(Dwell_Time2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DwellTime2_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DwellTime2_Pctiles)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Histogram of Dwell_Time2.


DwellTime2_HistDen <- ggplot(AllDays_NewOrder, aes(x = Dwell_Time2, y = ..density..)) +
  geom_histogram(binwidth = 1, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(1, 25), ylim = c(0, 0.05)
                 ) +
  xlab("Time a Bus Stays at a Stop (sec)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Long a Bus Stays at a Stop"
                          # ,atop(italic("xxxxx"),"")
                         )
                    )
         )

DwellTime2_HistDen

Investigation of Dwell_Time2 (how long the bus is at a stop).

Looking at some weirdly long Dwell_Time2 values.


View(arrange(AllDays_NewOrder,
             desc(Dwell_Time2)
            )
    )


# examples of extremely large Dwell_Time2s
View(filter(AllDays_NewOrder,
            (RowNum_OG >= 292669 & RowNum_OG <= 292689) | # 292679
                (RowNum_OG >= 531057 & RowNum_OG <= 531077) | # 531067
                (RowNum_OG >= 1388627 & RowNum_OG <= 1388647) | # 1388637
                (RowNum_OG >= 1645711 & RowNum_OG <= 1645731) # 1645721
           )
    )


View(filter(AllDays_NewOrder,
            Dwell_Time2 == 0
           )
    )

Investigation of Delta_Time (how early or late the bus is).

View(DT2_Pctiles): 94% of Delta_Time values are between -236 seconds and 1,259 seconds. Roughly 66% of records are within 5 min late and 5 min early…but some weird (e.g., almost 50 minute late or 40 minute early) Delta_Times exist.

Note that Delta_Time is the difference from the scheduled bus arrival. So if two buses are scheduled to arrive at a destination at 10:00pm and 10:20pm, and if the 10:20pm bus has a Delta_Time of 5 minutes, there are 25 minutes between bus arrivals at the stop.

Also note that based on a comment at https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/, the Delta_Time values don’t appear to coincide with published bus schedules (e.g., the X2 departing every 8 minutes during peak hours).


DeltTime_Ntile <- as.data.frame(AllDays_NewOrder$Delta_Time) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Delta_Time, 100),
         MinR = min_rank(AllDays_NewOrder$Delta_Time),
         PctR = percent_rank(AllDays_NewOrder$Delta_Time),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTime_Ntile)[1] <- "Delta_Time"
str(DeltTime_Ntile)

DeltTime_Ntile_Rows <- nrow(DeltTime_Ntile)

View(tail(DeltTime_Ntile, 500))


DeltTime_Pctiles <- group_by(DeltTime_Ntile,
                             PctR_Round
                            ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTime_Pctiles)
DeltTime_Pctiles

# ~66% of rows are between 5 min late and 5 min early
nrow(filter(AllDays_NewOrder,
            Delta_Time >= -300 &
              Delta_Time <= 300
           )
    ) / nrow(AllDays_NewOrder)


# examples of weird large Delta_Times
View(filter(AllDays_NewOrder,
            Delta_Time < -4202 |
              Delta_Time > 1705
           ) %>% 
       arrange(desc(Delta_Time)
              )
    )

Investigation of Delta_Time (how early or late the bus is).

Delta_Time histogram.


DeltTime_HistDen <- ggplot(AllDays_NewOrder, aes(x = (Delta_Time / 60),
                                                 y = ..density..
                                                )
                          ) +
  geom_histogram(binwidth = (5/60), fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(-5, 5)) +
  xlab("Bus Lateness (min)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Early/Late a Bus Is",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_HistDen

Investigation of Delta_Time (how early or late the bus is).

Delta_Time boxplot.


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(AllDays_NewOrder,
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(Delta_Time / 60, na.rm = TRUE)
                     )

DeltTime_BoxPlot <- ggplot(AllDays_NewOrder,
                           aes(factor(Event_Time_HrGroup),
                               Delta_Time / 60,
                               fill = factor(Event_Time_HrGroup)
                              )
                          ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE) + 
  # coord_cartesian(ylim = c(-300, 1200)) +
  coord_cartesian(ylim = c(-5, 20)) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  xlab("Hour Group") + 
  ylab("Bus Lateness (minutes)") + 
  theme(legend.position="none", axis.text.x = element_text(angle=45)) + 
  #theme(legend.position="right", axis.text.x = element_blank()) + 
  ggtitle(expression(atop("How Early/Late is the Bus (by Hour Group)",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_BoxPlot

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. First let’s get some “rank” stats.


View(DeltTime_Pctiles)
DeltTime_Pctiles


DeltTimeAbs_Ntile <- as.data.frame(abs(AllDays_NewOrder$Delta_Time)) %>% 
  mutate(Pctile = ntile(abs(AllDays_NewOrder$Delta_Time), 100),
         MinR = min_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR = percent_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTimeAbs_Ntile)[1] <- "Delta_Time_Abs"
str(DeltTimeAbs_Ntile)

DeltTimeAbs_Ntile_Rows <- nrow(DeltTimeAbs_Ntile)

View(tail(DeltTimeAbs_Ntile, 500))


DeltTimeAbs_Pctiles <- group_by(DeltTimeAbs_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time_Abs),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTimeAbs_Pctiles)
DeltTimeAbs_Pctiles

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. Then let’s calculate the percentage of buses that are 10 minutes (or more) late/early.


HrGroup_DeltaTime_All <- group_by(AllDays_NewOrder,
                                  Event_Time_HrGroup
                                 ) %>% 
  summarise(EventAll_Cnt = n()
           )

str(HrGroup_DeltaTime_All)
View(HrGroup_DeltaTime_All)


HrGroup_DeltaTime_Above10Min <- filter(AllDays_NewOrder,
                                       abs(Delta_Time) >= 600
                                      ) %>% 
  group_by(Event_Time_HrGroup) %>% 
  summarise(EventAbove10_Cnt = n()
           )

str(HrGroup_DeltaTime_Above10Min)
View(HrGroup_DeltaTime_Above10Min)


HrGroup_DeltaTimeCompare <- inner_join(HrGroup_DeltaTime_Above10Min,
                                       HrGroup_DeltaTime_All,
                                       by = c("Event_Time_HrGroup" = "Event_Time_HrGroup")
                                      ) %>% 
  mutate(PctEventsAbove10 = EventAbove10_Cnt / EventAll_Cnt)

View(HrGroup_DeltaTimeCompare)

Investigation of Delta_Time (how early or late the bus is).

Quickly plot these “extreme” Delta_Times.


DeltTime_Above10_Cols <- ggplot(HrGroup_DeltaTimeCompare,
                                aes(factor(Event_Time_HrGroup),
                                    PctEventsAbove10
                                   )
                               ) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_text(aes(label = format(round(PctEventsAbove10, digits = 2),
                               nsmall = 2
                              )
               ),
            size = 3,
            nudge_y = (HrGroup_DeltaTimeCompare$PctEventsAbove10 * -0.1)
           ) +
  # coord_cartesian(xlim = c(-5, 5)) +
  xlab("Hour Group") + 
  ylab("Percent of All Bus Arrivals") +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  ggtitle(expression(atop("When is a Bus 10+ Minutes Late/Early"
                          # ,atop(italic("positive values are late arrivals"),
                          #      ""
                          #     )
                         )
                    )
         )

DeltTime_Above10_Cols

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Correlation.


DwellTDeltaT_Corr <- as.matrix(cor(x = AllDays_NewOrder$Dwell_Time2,
                                   y = AllDays_NewOrder$Delta_Time,
                                   use = "pairwise"
                                  )
                               )

DwellTDeltaT_Corr

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Next, let’s get a sample of data for plotting. Let’s do this for the full dataset (AllDays_NewOrder).


AllDays_NewOrder_10PctSamp <- sample_frac(AllDays_NewOrder, 0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "AllData")

str(AllDays_NewOrder_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Let’s also get a sample of data for plotting, but with a datset that removes outliers.


View(DeltTime_Pctiles)
View(DwellTime2_Pctiles)

AllDays_NewOrder_NoExtremes_10PctSamp <- filter(AllDays_NewOrder,
                                                between(Delta_Time, -402, 1705) & # removes about 2% of Delta_Time values
                                                  between(Dwell_Time2, 1, 63)  # removes about 2% of Dwell_Time2 values
                                               ) %>% 
  sample_frac(0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "OutliersRemoved")

str(AllDays_NewOrder_NoExtremes_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does not remove outliers.


DwellTDeltaT_Scatter <- ggplot(AllDays_NewOrder_10PctSamp,
                               aes(Dwell_Time2, Delta_Time)
                              ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "red") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 2200,
           y = 600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(no outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(no outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does remove outliers.


DwellTDeltaT_Scatter_NoExtremes <- ggplot(AllDays_NewOrder_NoExtremes_10PctSamp,
                                          aes(Dwell_Time2, Delta_Time)
                                         ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 50,
           y = -475,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(2% of outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(2% of outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_NoExtremes

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from both datasets together.


CombinedData <- rbind(AllDays_NewOrder_10PctSamp,
                      AllDays_NewOrder_NoExtremes_10PctSamp
                     )

CombinedData$DataSet <- factor(CombinedData$DataSet)

str(CombinedData)


DwellTDeltaT_Scatter_Combined <- ggplot(CombinedData,
                                        aes(x = Dwell_Time2,
                                            y = Delta_Time,
                                            colour = DataSet
                                           )
                                       ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  coord_cartesian(xlim = c(0, 500), ylim = c(-1000, 2000)
                 ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "AllData"
                           ),
              method = "lm",
              colour = "red"
             ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "OutliersRemoved"
                           ),
              method = "lm",
              colour = "blue"
             ) +
  # facet_wrap( ~ DataSet, ncol = 2) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -800,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  theme(legend.position = "bottom") +
  labs(title = "Lateness vs Time at Stop",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
                          # ,atop(italic("2% of outliers removed"),
                          #       ""
                          #      )
         #                 )
         #            )
         # )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_Combined

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayBmb3IgV01BVEEgTWV0cm9idXMgRGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZm9yIGFuYWx5c2lzIHVzaW5nIGRhdGEgb24gdGhlIERDIEJ1cyBTeXN0ZW0gKFdNQVRBIE1ldHJvYnVzKS4gIFRoZSBkYXRhIHdlcmUgb2J0YWluZWQgaGVyZToKCmh0dHBzOi8vcGxhbml0bWV0cm8uY29tLzIwMTYvMTEvMTYvZGF0YS1kb3dubG9hZC1tZXRyb2J1cy12ZWhpY2xlLWxvY2F0aW9uLWRhdGEvCgpDb250cm9sICsgQWx0ICsgU2hpZnQgKyBtID0gcmVuYW1lIGluIHNjb3BlCgpMb2FkIHRoZSBwYWNrYWdlcyB0byBiZSB1c2VkLgpgYGB7ciBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KCmxpYnJhcnkoImpzb25saXRlIikKbGlicmFyeSgic3FsZGYiKQpsaWJyYXJ5KCJ0Y2x0ayIpCmxpYnJhcnkoInRpZHlyIikKbGlicmFyeSgicGx5ciIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgibWFncml0dHIiKQpsaWJyYXJ5KCJzdHJpbmdyIikKbGlicmFyeSgiZGF0YS50YWJsZSIpCmxpYnJhcnkoImx1YnJpZGF0ZSIpCmxpYnJhcnkoImdlb3NwaGVyZSIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJnZ21hcCIpCmxpYnJhcnkoImdndmlzIikKbGlicmFyeSgicmJva2VoIikKbGlicmFyeSgicmdkYWwiKQpsaWJyYXJ5KCJtYXB0b29scyIpCgpgYGAKCgpHZXQgdGhlIEJ1cyBkYXRhLgoKRmlyc3QgbGV0J3MgY2hlY2sgdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgpgYGB7cn0KCmdldHdkKCkKCmBgYAoKClRoZW4sIGFjdHVhbGx5IGdldCB0aGUgZGF0YS4KYGBge3IgZWNobyA9IEZBTFNFfQoKc2V0d2QoIi9Vc2Vycy9tZHR1cnNlL0Rlc2t0b3AvQW5hbHl0aWNzL0RDTWV0cm9CdXMvQnVzIEFWTCBPY3QgMjAxNiIpCgpmb3IgKGkgaW4gMzo3KXsKICBhc3NpZ24ocGFzdGUwKCJPY3QwIiwgaSwgIlJhdyIpLAogICAgICAgICByZWFkLmRlbGltKHBhc3RlMCgiMjAxNjEwMCIsIGksICJNZXRyb2J1c0FWTC50eHQiKSwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9IE5VTEwKICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgbWVzc2FnZSgiT2N0MCIsIGksICJSYXciKQogCiAgc3RyKGdldChwYXN0ZTAoIk9jdDAiLCBpLCAiUmF3IikKICAgICAgICAgKQogICAgICkKICB9CgpgYGAKCgpQdXQgdGhlIGRhaWx5IGRhdGEgdG9nZXRoZXIuCmBgYHtyfQoKQWxsRGF5cyA8LSBiaW5kX3Jvd3MobGlzdChPY3QwM1JhdywgT2N0MDRSYXcsIE9jdDA1UmF3LCBPY3QwNlJhdywgT2N0MDdSYXcpLAogICAgICAgICAgICAgICAgICAgICAuaWQgPSBjKCJncm91cCIpCiAgICAgICAgICAgICAgICAgICAgKQojIGRpbShBbGxEYXlzKQpzdHIoQWxsRGF5cykKCmBgYAoKCkRlbGV0aW5nIG9sZCBkYXRhIGZyYW1lcy4KYGBge3J9Cgpmb3IgKGkgaW4gMzo3KXsKICBybShsaXN0ID0gbHMocGF0dGVybiA9IHBhc3RlMCgiT2N0MCIsIGksICJSYXciKQogICAgICAgICAgICAgICkKICAgICkKICAKICBtZXNzYWdlKCJEZWxldGluZyBPY3QwIiwgaSwgIlJhdyIpCiAgfQoKYGBgCgoKVXBkYXRpbmcgdmFyaWFibGUgdHlwZXMuCgpUaGVuLCBzb3J0aW5nIHRoZSBkYXRhIGFuZCBhZGRpbmcgYSBSb3dOdW1iZXIgKHRvIGJlIHVzZWQgZm9yIGlkZW50aWZ5aW5nIHJvd3MgbGF0ZXIgaW4gdGhlIGFuYWx5c2VzLikKYGBge3J9CgpybShpKQoKCkFsbERheXMkZ3JvdXAgPC0gZmFjdG9yKEFsbERheXMkZ3JvdXApCkFsbERheXMkUm91dGVfRGlyZWN0aW9uIDwtIGZhY3RvcihBbGxEYXlzJFJvdXRlX0RpcmVjdGlvbikKQWxsRGF5cyRFdmVudF9UaW1lIDwtIGFzLlBPU0lYY3QoQWxsRGF5cyRFdmVudF9UaW1lLCBmb3JtYXQgPSAiJW0tJWQtJXkgJUk6JU06JVMgJXAiKQpBbGxEYXlzJERlcGFydHVyZV9UaW1lIDwtIGFzLlBPU0lYY3QoQWxsRGF5cyREZXBhcnR1cmVfVGltZSwgZm9ybWF0ID0gIiVtLSVkLSV5ICVJOiVNOiVTICVwIikKCnN0cihBbGxEYXlzKQoKCkFsbERheXNfU29ydGVkIDwtIGFycmFuZ2UoQWxsRGF5cywKICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFJvd051bV9PRyA9IHJvd19udW1iZXIoKSAjIHRoaXMgaXMgdXNlZnVsIGluIGlkZW50aWZ5IHRoZSByb3cgbGF0ZXIgb24KICAgICAgICApCgpybShBbGxEYXlzKQpzdHIoQWxsRGF5c19Tb3J0ZWQpCgojIFZpZXcoaGVhZChBbGxEYXlzX1NvcnRlZCwgMTAwKSkKCmBgYAoKCkluc3BlY3RpbmcgdGhlIHZhbHVlcyBvZiBTdG9wX0lELCBhbmQgZmluZGluZyB0aGF0IGl0IGNhbiB0YWtlIHRoZSB2YWx1ZXMgIiIgKGJsYW5rKSBhbmQgIk5VTEwiLgpgYGB7cn0KClZpZXcoZ3JvdXBfYnkoQWxsRGF5c19Tb3J0ZWQsCiAgICAgICAgICAgICAgU3RvcF9JRAogICAgICAgICAgICAgKSAlPiUgCiAgICAgICBzdW1tYXJpc2UoCiAgICAgICAgIENudCA9IG4oKQogICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoU3RvcF9JRCkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfU29ydGVkLAogICAgICAgICAgICBpcy5uYShTdG9wX0lEKSB8CiAgICAgICAgICAgICAgU3RvcF9JRCA9PSAiIiB8CiAgICAgICAgICAgICAgU3RvcF9JRCA9PSAiTlVMTCIKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoU3RvcF9EZXNjKQogICAgKQoKYGBgCgoKQ3JlYXRpbmcgYSB0YWJsZSBvZiBkaXN0aW5jdCBTdG9wX0Rlc2MgdmFsdWVzIHdoZW4gU3RvcF9JRCBpcyAiIiAoYmxhbmspIG9yICJOVUxMIi4KYGBge3J9CgpTdG9wSURfTmV3IDwtIGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKICAgICAgICAgICAgICAgICAgICAgaXMubmEoU3RvcF9JRCkgfAogICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQgPT0gIiIgfAogICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQgPT0gIk5VTEwiCiAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2VsZWN0KFN0b3BfSUQsIFN0b3BfRGVzYykgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIGFycmFuZ2UoU3RvcF9JRCwgU3RvcF9EZXNjKSAlPiUgCiAgbXV0YXRlKFN0b3BJRF9OZXcgPSAxOm5yb3coLikKICAgICAgICApCgpWaWV3KFN0b3BJRF9OZXcpCgpgYGAKCgpDcmVhdGluZyBhIGZ1bGwgdXBkYXRlZCB0YWJsZSBieSBmaWxsaW5nIGluIFN0b3BJRF9OZXcgZm9yIHdoZW4gU3RvcF9JRCBpcyAiIiAoYmxhbmspIG9yIE5VTEwuCmBgYHtyfQoKQWxsRGF5c19TdG9wSUROZXcgPC0gbGVmdF9qb2luKEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFN0b3BJRF9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlN0b3BfRGVzYyIgPSAiU3RvcF9EZXNjIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFN0b3BJRF9DbGVhbiA9IGlmZWxzZShpcy5uYShTdG9wSURfTmV3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfTmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKFN0b3BJRF9OZXcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfT0siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfQmFkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgpybShTdG9wSURfTmV3KQpybShBbGxEYXlzX1NvcnRlZCkKc3RyKEFsbERheXNfU3RvcElETmV3KQoKIyBWaWV3KHRhaWwoQWxsRGF5c19TdG9wSUROZXcsIDUwMCkpCiMgVmlldyhmaWx0ZXIoQWxsRGF5c19TdG9wSUROZXcsCiMgICAgICAgICAgICAgU3RvcF9EZXNjID09ICJNRVRST1dBWSBBTk5OT1VDRU1OVCBDT1JSIgojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgoKTGF0IExvbmcgc3RhdHMgZm9yIHB1bGxpbmcgaW4gWmlwIGNvZGVzIGxhdGVyLgpgYGB7cn0KCkxMX1N0YXRzIDwtIGdyb3VwX2J5KEFsbERheXNfU3RvcElETmV3LAogICAgICAgICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoTGF0X01lYW4gPSBtZWFuKExhdGl0dWRlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBMYXRfTWVkID0gbWVkaWFuKExhdGl0dWRlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBMbmdfTWVhbiA9IG1lYW4oTG9uZ2l0dWRlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBMbmdfTWVkID0gbWVkaWFuKExvbmdpdHVkZSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShMYXRfTWVhTGVzc01lZCA9IExhdF9NZWFuIC0gTGF0X01lZCwKICAgICAgICAgTG5nX01lYUxlc3NNZWQgPSBMbmdfTWVhbiAtIExuZ19NZWQsCiAgICAgICAgIFJvd051bSA9IHJvd19udW1iZXIoKQogICAgICAgICkKCnN0cihMTF9TdGF0cykKc3VtbWFyeShMTF9TdGF0cykKClZpZXcoaGVhZChhcnJhbmdlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICBMYXRfTWVhTGVzc01lZAogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIGRlc2MoTGF0X01lYUxlc3NNZWQpCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpWaWV3KGhlYWQoYXJyYW5nZShMTF9TdGF0cywKICAgICAgICAgICAgICAgICAgTG5nX01lYUxlc3NNZWQKICAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClZpZXcoaGVhZChhcnJhbmdlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICBkZXNjKExuZ19NZWFMZXNzTWVkKQogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKYGBgCgoKUHVsbGluZyBpbiBaaXAgQ29kZSBkYXRhIGZyb20gYXBpLmdlb25hbWVzLm9yZy4KYGBge3J9CgojIFVSTCBFWEFNUExFOgojIGh0dHA6Ly9hcGkuZ2VvbmFtZXMub3JnL2ZpbmROZWFyYnlQb3N0YWxDb2Rlc0pTT04/bGF0PTM4Ljg5NTYwJmxuZz0tNzYuOTQ4NzMmcmFkaXVzPTAmdXNlcm5hbWU9c3VwZXJtZGF0Cgp1cmxfMSA8LSAiaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcvZmluZE5lYXJieVBvc3RhbENvZGVzSlNPTj9sYXQ9Igp1cmxfMiA8LSAiJmxuZz0iCnVybF8zIDwtICImcmFkaXVzPTAmdXNlcm5hbWU9Igp1c2VybmFtZSA8LSAic3VwZXJtZGF0IgoKCiMgbmVlZCB0byBncm91cCBpbiBidW5jaGVzIGFzIGh0dHA6Ly9hcGkuZ2VvbmFtZXMub3JnIGxpbWl0cyBwdWxscyB0byAyMDAwIHBlciBob3VyCgoKIyMjIyMgU3RvcmUgZXZlcnl0aGluZyBpbiBtdWx0aXBsZSBsaXN0cwpwYWdlczEgPC0gbGlzdCgpCgoKc3lzdGVtLnRpbWUoCiMgICBmb3IoaiBpbiAwOjUpewojICAgZm9yKGsgaW4gKChtYXhfcm93X3Blcl9ocipqKSArIDEpOihtYXhfcm93X3Blcl9ociooaisxKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICl7CiMgICAgIAojICAgfQojIH0KZm9yKGkgaW4gMToxMDAwKXsKICBsYXQgPC0gZmlsdGVyKExMX1N0YXRzLAogICAgICAgICAgICAgICAgUm93TnVtID09IGkKICAgICAgICAgICAgICAgKSAlPiUKICAgIHNlbGVjdChMYXRfTWVkKQogIAogIGxuZyA8LSBmaWx0ZXIoTExfU3RhdHMsCiAgICAgICAgICAgICAgICBSb3dOdW0gPT0gaQogICAgICAgICAgICAgICApICU+JQogICAgc2VsZWN0KExuZ19NZWQpCiAgCiAgQVBJRGF0YTEgPC0gZnJvbUpTT04ocGFzdGUwKHVybF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybF8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVybF8zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VybmFtZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgZmxhdHRlbiA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICkKICAKICBtZXNzYWdlKCJSZXRyaWV2aW5nIFppcCBDb2RlICIsIGkpCiAgCiAgcGFnZXMxW1tpXV0gPC0gQVBJRGF0YTEkcG9zdGFsQ29kZXMKICAKICAjIFN5cy5zbGVlcCgzOTAwKQp9CikKCiMgY2xhc3MoQVBJRGF0YTEpCiMgY2xhc3MoQVBJRGF0YTIkcG9zdGFsQ29kZXMpCiMgc3RyKEFQSURhdGExKQojIGhlYWQoQVBJRGF0YTEpCiMgY2xhc3MocGFnZXMxKQojIHN0cihwYWdlczEpCiMgbnJvdyhwYWdlMSkKIyBuY29sKHBhZ2UxKQojIHBhZ2VzMVtbMTE5OV1dCiMgcGFnZXMxW1syMDUxXV0KCgojIyMjIyBDb21iaW5lIHRoZSBsaXN0cyBpbnRvIG9uZSBwYWdlClppcHMxIDwtIHJiaW5kLnBhZ2VzKHBhZ2VzMVtzYXBwbHkocGFnZXMxLCBsZW5ndGgpID4gMF0pCgoKIyMjIyMgQ29tYmluZSBhbGwgcGFnZXMKWmlwc19BbGwgPC0gYmluZF9yb3dzKFppcHMwLAogICAgICAgICAgICAgICAgICAgICAgWmlwczEsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMiwKICAgICAgICAgICAgICAgICAgICAgIFppcHMzLAogICAgICAgICAgICAgICAgICAgICAgWmlwczQsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNSwKICAgICAgICAgICAgICAgICAgICAgIFppcHM2LAogICAgICAgICAgICAgICAgICAgICAgWmlwczcsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzOCwKICAgICAgICAgICAgICAgICAgICAgIFppcHM5LAogICAgICAgICAgICAgICAgICAgICAgWmlwczEwLAogICAgICAgICAgICAgICAgICAgICAgIyBaaXBzMV9hLAogICAgICAgICAgICAgICAgICAgICAgLmlkID0gImlkIgogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVW5pcXVlTGF0TG5nID0gcGFzdGUobGF0LCBsbmcsIHNlcCA9ICJfXyIpCiAgICAgICAgKQoKIyBzdHIoWmlwc19BbGwpCiMgVmlldyhoZWFkKFppcHNfQWxsKSkKCgojIHN0cihMTF9TdGF0cykKTExfU3RhdHNfVW5xTGF0TG5nIDwtIG11dGF0ZShMTF9TdGF0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVbmlxdWVMYXRMbmcgPSBwYXN0ZShMYXRfTWVkLCBMbmdfTWVkLCBzZXAgPSAiX18iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIyBzdHIoTExfU3RhdHNfVW5xTGF0TG5nKQojIFZpZXcoaGVhZChMTF9TdGF0c19VbnFMYXRMbmcpKQoKCkxMX1N0YXRzWmlwcyA8LSBsZWZ0X2pvaW4oTExfU3RhdHNfVW5xTGF0TG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgIFppcHNfQWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiVW5pcXVlTGF0TG5nIiA9ICJVbmlxdWVMYXRMbmciKQogICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKExMX1N0YXRzWmlwcykKIyBWaWV3KGhlYWQoTExfU3RhdHNaaXBzKSkKCiMgTm90IHN1cmUgd2hleSB0aGVzZSBjb3VsZG4ndCBiZSBmb3VuZCAod2h5IHRoZXkncmUgTkEpClZpZXcoZmlsdGVyKExMX1N0YXRzWmlwcywKICAgICAgICAgICAgaXMubmEocG9zdGFsQ29kZSkKICAgICAgICAgICApCiAgICApCgpgYGAKCgpKb2luIHRvIGNyZWF0ZSBvbmUgZGF0YXNldCB0aGF0IGFsc28gaW5jbHVkZXMgWmlwIHZhcmlhYmxlcy4KYGBge3J9CgpybSh1cmxfMSwgdXJsXzIsIHVybF8zLCB1c2VybmFtZSwgcGFnZXMwLCBwYWdlczEsIHBhZ2VzMiwgcGFnZXMzLCBwYWdlczQsIHBhZ2VzNSwgcGFnZXM2LCBwYWdlczcsIHBhZ2VzOCwgcGFnZXM5LCBwYWdlczEwLCBpLCBsYXQsIGxuZywgQVBJRGF0YTAsIEFQSURhdGExLCBBUElEYXRhMiwgQVBJRGF0YTMsIEFQSURhdGE0LCBBUElEYXRhNSwgQVBJRGF0YTYsIEFQSURhdGE3LCBBUElEYXRhOCwgQVBJRGF0YTksIEFQSURhdGExMCwgTExfU3RhdHMsIExMX1N0YXRzX1VucUxhdExuZykKCgpBbGxEYXlzX1ppcHMgPC0gbGVmdF9qb2luKEFsbERheXNfU3RvcElETmV3LAogICAgICAgICAgICAgICAgICAgICAgICAgIExMX1N0YXRzWmlwcywKICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlN0b3BJRF9DbGVhbiIgPSAiU3RvcElEX0NsZWFuIikKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHJlbmFtZShTdG9wX1N0YXRlID0gYWRtaW5Db2RlMSwKICAgICAgICAgU3RvcF9Db3VudHkgPSBhZG1pbk5hbWUyLAogICAgICAgICBTdG9wX0NpdHkgPSBwbGFjZU5hbWUsCiAgICAgICAgIFN0b3BfWmlwID0gcG9zdGFsQ29kZQogICAgICAgICApCgpybShBbGxEYXlzX1N0b3BJRE5ldywgTExfU3RhdHNaaXBzKQpzdHIoQWxsRGF5c19aaXBzKQoKYGBgCgoKVXBkYXRpbmcgdmFyaWFibGUgdHlwZXMuCmBgYHtyfQoKQWxsRGF5c19aaXBzJFN0b3BfU3RhdGUgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX1N0YXRlKQpBbGxEYXlzX1ppcHMkU3RvcF9Db3VudHkgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX0NvdW50eSkKQWxsRGF5c19aaXBzJFN0b3BfWmlwIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkU3RvcF9aaXApCkFsbERheXNfWmlwcyRTdG9wX0NpdHkgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX0NpdHkpCgpBbGxEYXlzX1ppcHMkZGlzdGFuY2UgPC0gYXMubnVtZXJpYyhBbGxEYXlzX1ppcHMkZGlzdGFuY2UpCkFsbERheXNfWmlwcyRjb3VudHJ5Q29kZSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJGNvdW50cnlDb2RlKQpBbGxEYXlzX1ppcHMkYWRtaW5OYW1lMSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJGFkbWluTmFtZTEpCgpzdHIoQWxsRGF5c19aaXBzKQoKYGBgCgoKRmVhdHVyZSBlbmdpbmVlcmluZy4KCkluc3BlY3RpbmcgaW5jaWRlbmNlcyBvZiBjb25zZWN1dGl2ZSBTdG9wX0lEcy4gVGhpcyBpcyBkb25lIGJlY2F1c2UgaW52ZXN0aWdhdGlvbiBzaG93ZWQgdGhhdCBtYW55IGNvbnNldXRpdmUgZXZlbnRzIG9jY3VyciBhdCB0aGUgc2FtZSBTdG9wX0lELCBidXQgd2l0aCB2YXJpb3VzIER3ZWxsX1RpbWVzLCBPZG9tZXRlcl9EaXN0YW5jZXMsIGV0Yy4gIEFsbCBvZiB3aGljaCBhZmZlY3QgY2FsY3VsYXRpb25zIGFuZCBhbmFseXNlcy4KCkNyZWF0ZSBkYXRhIG9uIHRoZSBydW5zIChjb25zZWN1dGl2ZSBTdG9wX0lEcykuCmBgYHtyfQoKU3RvcElEX1J1bnMgPC0gcmxlKEFsbERheXNfWmlwcyRTdG9wSURfQ2xlYW4pCgpTdG9wSURfUnVucyRlbmRzIDwtIGN1bXN1bShTdG9wSURfUnVucyRsZW5ndGhzKQoKU3RvcElEX1J1bnMkc3RhcnRzIDwtIGlmZWxzZShpcy5uYShsYWcoU3RvcElEX1J1bnMkZW5kcykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWcoU3RvcElEX1J1bnMkZW5kcykgKyAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoU3RvcElEX1J1bnMpCiMgY2xhc3MoU3RvcElEX1J1bnMpCiMgCiMgU3RvcElEX1J1bnNfZGYgPC0gZGF0YS5mcmFtZSh1bmNsYXNzKFN0b3BJRF9SdW5zKSkKIyBzdHIoU3RvcElEX1J1bnNfZGYpCiMgY2xhc3MoU3RvcElEX1J1bnNfZGYpCiMgcm0oU3RvcElEX1J1bnNfZGYpCgpgYGAKCgpUcnlpbmcgdG8gbGluayBkYXRhIG9uIFJ1bnNHcm91cHMgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YSAoQWxsRGF5c19Tb3J0ZWQpLiBUaGUgZ29hbCBpcyB0byBzZWxlY3Qgb25seSBvbmUgcmVjb3JkIHBlciBSdW5zR3JvdXAgLSB0aGF0IGJlaW5nIHRoZSByZWNvcmQgd2l0aCB0aGUgbG9uZ2VzdCBEd2VsbF9UaW1lLgoKSSBhdHRlbXB0ZWQgdGhpcyBjb21wdXRhdGlvbiB1c2luZyBib3RoIGRhdGEuZnJhbWVzIChkcGx5cikgYW5kIGRhdGEudGFibGVzIChkYXRhLnRhYmxlKS4gSG93ZXZlciwgd2l0aCAyLDgwOSwwNjIgcm93cyBpbiBvbmUgZGF0YXNldCBhbmQgMywxMTksNDQzIHJvd3MgaW4gdGhlIG90aGVyIGRhdGFzZXQsIHRoZSBjdXJyZW50IGNvbXB1dGF0aW9uIHRpbWUgaXMgb3ZlciA1IGRheXMuLi5zbyBJJ20gdHJ5aW5nIGEgZGlmZmVyZW50IHN0cmF0ZWd5IHRvIG9ubHkgc2VsZWN0IHRoZSBmaXJzdCByZWNvcmQgaW4gYSBydW4uCmBgYHtyfQoKIyBDcmVhdGUgYSBSdW5zR3JvdXAgdmFyaWFibGUgZm9yIGVhY2ggcnVuCiMgU3RvcElEX1J1bnNfZGYkUnVuc0dyb3VwIDwtIHBhc3RlMCgiZyIsIHNlcSgxOm5yb3coU3RvcElEX1J1bnNfZGYpCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojIAojIHN0cihTdG9wSURfUnVuc19kZikKIyBoZWFkKFN0b3BJRF9SdW5zX2RmLCAyNSkKIyB0YWlsKFN0b3BJRF9SdW5zX2RmLCAyNSkKIyAKIyBTdG9wSURfUnVuc19kZiA8LSBTdG9wSURfUnVuc19kZiAlPiUgCiMgICBtdXRhdGUoUm93TnVtID0gcm93X251bWJlcigpCiMgICAgICAgICApCiMgCiMgc3RyKFN0b3BJRF9SdW5zX2RmKQojIGhlYWQoU3RvcElEX1J1bnNfZGYsIDI1KQojIHRhaWwoU3RvcElEX1J1bnNfZGYsIDI1KQojIAojIAojICMgQ29udmVydGluZyB0byBkYXRhLnRhYmxlcyBmb3IsIGhvcGVmdWxseSwgaW1wcm92ZWQgcGVyZm9ybWFuY2UgKHNwZWVkKSBpbiBjb21wdXRhdGlvbgojIFN0b3BJRF9SdW5zX2R0IDwtIGRhdGEudGFibGUoU3RvcElEX1J1bnNfZGYpCiMgc2V0a2V5KFN0b3BJRF9SdW5zX2R0LCBSb3dOdW0pCiMgc3RyKFN0b3BJRF9SdW5zX2R0KQojIAojIEFsbERheXNfU29ydGVkX2R0IDwtIGRhdGEudGFibGUoQWxsRGF5c19Tb3J0ZWQpCiMgc2V0a2V5KEFsbERheXNfU29ydGVkX2R0LCBSb3dOdW1fT0cpCiMgc3RyKEFsbERheXNfU29ydGVkX2R0KQojICMgcm0oQWxsRGF5c19Tb3J0ZWRfZHQpCiMgCiMgCiMgIyBBY3R1YWwgbG9vcCB0byBwZXJmb3JtIHRoZSBjb21wdXRhdGlvbnMgYW5kIGxpbmsgdG8gb3JpZ2luYWwgZGF0YSAoQWxsRGF5c19Tb3J0ZWRfZHQpCiMgR3JvdXBEYXRhIDwtIGxpc3QoKQojIGZvcihpIGluIDE6bnJvdyhTdG9wSURfUnVuc19kdCkKIyAgICApIHsKIyAgIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGkpLAojICAgICAgICAgICAgU3RvcElEX1J1bnNfZHRbUm93TnVtID09IGksIFJ1bnNHcm91cF0KIyAgICAgICAgICAgKQojIAojICAgICAjIyMjIyAgVGhlIGNvZGUgYmVsb3cgaXMgdGhlIHNhbWUgY29kZSBhcyBhYm92ZSwgYnV0IGRvbmUgd2l0aCBkcGx5ciAgIyMjIyMKIyAKIyAgICAgIyBhc3NpZ24ocGFzdGUwKCJncm91cF8iLCBpKSwKIyAgICMgICAgICAgIGZpbHRlcihTdG9wSURfUnVuc19kZiwKIyAgICMgICAgICAgICAgICAgICBSb3dOdW0gPT0gaQojICAgIyAgICAgICAgICAgICAgKSAlPiUgCiMgICAjICAgICAgICAgIHNlbGVjdChSdW5zR3JvdXApCiMgICAjICAgICAgICkKIyAKIyAgIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGksICJfc3RhcnQiKSwKIyAgICAgICAgICBTdG9wSURfUnVuc19kdFtSb3dOdW0gPT0gaSwgc3RhcnRzXQojICAgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9lbmQiKSwKIyAgICAgICAgICBTdG9wSURfUnVuc19kdFtSb3dOdW0gPT0gaSwgZW5kc10KIyAgICAgICAgICkKIyAKIyAgIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGksICJfcm93cyIpLAojICAgICAgICAgIEFsbERheXNfU29ydGVkX2R0W1Jvd051bV9PRyA+PSBhcy5udW1lcmljKGdldChwYXN0ZTAoImdyb3VwXyIsIGksICJfc3RhcnQiKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgojICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvd051bV9PRyA8PSBhcy5udW1lcmljKGdldChwYXN0ZTAoImdyb3VwXyIsIGksICJfZW5kIikKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bnNHcm91cCA6PSBhcy5jaGFyYWN0ZXIoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgXQojIAojICAgICAjIyMjIyAgVGhlIGNvZGUgYmVsb3cgaXMgdGhlIHNhbWUgYXMgdGhlIGNvZGUgYWJvdmUsIGJ1dCBkb25lIHdpdGggZHBseXIgICMjIyMjCiMgCiMgICAgICAgICAgIyBmaWx0ZXIoQWxsRGF5c19Tb3J0ZWQsCiMgICAgICAgICAgIyAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICBhcy5udW1lcmljKGdldChwYXN0ZTAoImdyb3VwXyIsIGksICJfc3RhcnQiKQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKIyAgICAgICAgICAjICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9lbmQiKQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICMgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICApICU+JSAKIyAgICAgICAgICAjICAgbXV0YXRlKFJ1bnNHcm91cCA9IGFzLmNoYXJhY3RlcihnZXQocGFzdGUwKCJncm91cF8iLCBpKQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgKQojICAgICAgICAgKQojIAojICAgR3JvdXBEYXRhW1tpXV0gPC0gZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9yb3dzIikpCiMgCiMgICBtZXNzYWdlKCJQcm9jZXNzaW5nIEdyb3VwICIsIGksICIgb2YgMiw4MDksMDYyIikKIyB9CiMgCiMgCiMgR3JvdXBEYXRhX2RmIDwtIHJiaW5kLmZpbGwoR3JvdXBEYXRhKQojIHN0cihHcm91cERhdGFfZGYpCiMgaGVhZChHcm91cERhdGFfZGYpCiMgdGFpbChHcm91cERhdGFfZGYpCiMgIyBybShHcm91cERhdGFfZGYpCiMgCiMgCiMgZ3JvdXBfMQojIGdyb3VwXzFfc3RhcnQKIyBncm91cF8xX2VuZAojIGdyb3VwXzFfcm93cwojIGdyb3VwXzJfcm93cwojIGdyb3VwXzNfcm93cwojIGdyb3VwXzUwX3Jvd3MKIyBzdHIoZ3JvdXBfNTBfcm93cykKIyBncm91cF8yODA5MDYyX3Jvd3MKIyBHcm91cERhdGFbWzFdXQojIEdyb3VwRGF0YVtbNTBdXQojIAojIAojICMjIyMjICBUZXN0aW5nIEFyZWEgKEJlbG93KSAgIyMjIyMKIyAjIyMjIyAgVGVzdGluZyBBcmVhIChCZWxvdykgICMjIyMjCiMgIyMjIyMgIFRlc3RpbmcgQXJlYSAoQmVsb3cpICAjIyMjIwojIAojICMgaGVhZChTdG9wSURfUnVucyRzdGFydHMsIDIwKQojICMgaGVhZChBbGxEYXlzX05ld09yZGVyJFN0b3BfSUQsIDIwKQojICMgCiMgIyAKIyAjIGRhdCA8LSBhcy5kYXRhLmZyYW1lKGMoMSwxLDcsNyw3LDksNiw4LDIsMiwyLDEsMSwxLDEsMSkpCiMgIyBjb2xuYW1lcyhkYXQpWzFdIDwtICJkYXQiCiMgIyByIDwtIHJsZShkYXQkZGF0KQojICMgZGF0JHJ1biA8LSByZXAociRsZW5ndGhzLCByJGxlbmd0aHMpCiMgIyBkYXQkcnVuTGFnIDwtIGxhZyhkYXQkcnVuKQojICMgZGF0JGNvbmQgPC0gcmVwKHIkdmFsdWVzLCByJGxlbmd0aHMpCiMgIyBkYXQKIyAjIFZpZXcoZGF0KQoKYGBgCgoKV2hlbiBjb25zZWN1dGl2ZSBTdG9wX0lEIG9jY3VycnMsIG9ubHkgdGFrZSB0aGUgZmlyc3Qgb2NjdXJyZW5jZS4gVGhpcyBpcyBkb25lIGJlY2F1c2UgdGhlIGNvbXB1dGF0aW9uIHRpbWUgdG8gc2VsZWN0IG9ubHkgdGhlIHJlY29yZCB3aXRoIHRoZSBsb25nZXN0IER3ZWxsX1RpbWUgZm9yIGVhY2ggcnVuIHdhcyB0b28gbG9uZyAob3ZlciA1IGRheXMpLgoKVGhpcyBpcyBwcm9iYWJseSBsZXNzIHRoYW4gaWRlYWwgd2l0aCByZWdhcmRzIHRvIER3ZWxsX1RpbWUsIGJ1dCBzaG91bGQgbm90IG1ha2UgbXVjaCBkaWZmZXJlbmNlIGZvciBjYWxjdWxhdGlvbnMgb2YgdHJhdmVsIHRpbWUsIHNwZWVkLCBldGMuCmBgYHtyfQoKQWxsRGF5c19GaXJzdFN0b3BJRCA8LSBBbGxEYXlzX1ppcHNbU3RvcElEX1J1bnMkc3RhcnRzLCBdCgpkaW0oQWxsRGF5c19aaXBzKQpkaW0oQWxsRGF5c19GaXJzdFN0b3BJRCkKCm5yb3coQWxsRGF5c19aaXBzKSAtIG5yb3coQWxsRGF5c19GaXJzdFN0b3BJRCkKCnJtKEFsbERheXNfWmlwcywgU3RvcElEX1J1bnMpCnN0cihBbGxEYXlzX0ZpcnN0U3RvcElEKQoKYGBgCgoKRmVhdHVyZSBlbmdpbmVlcmluZy4KCkNyZWF0aW5nIG5ldyB2YXJpYWJsZXMuCmBgYHtyfQoKQWxsRGF5c19BZGRWYXJzIDwtIG11dGF0ZShBbGxEYXlzX0ZpcnN0U3RvcElELAogICAgICAgICAgICAgICAgICAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlX01pID0gT2RvbWV0ZXJfRGlzdGFuY2UgLyA1MjgwLCAjNSwyODAgZmVldCBpbiAxIG1pbGUKICAgICAgICAgICAgICAgICAgICAgICAgICBEd2VsbF9UaW1lMiA9IGFzLm51bWVyaWMoRGVwYXJ0dXJlX1RpbWUgLSBFdmVudF9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX1lyID0gYXMuaW50ZWdlcih5ZWFyKEV2ZW50X1RpbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX010aCA9IGFzLmludGVnZXIobW9udGgoRXZlbnRfVGltZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF0ZSA9IGRheShFdmVudF9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RheSA9IHdkYXkoRXZlbnRfVGltZSwgbGFiZWwgPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyID0gaG91cihFdmVudF9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX01pbiA9IG1pbnV0ZShFdmVudF9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAgPSBmYWN0b3IoaWZlbHNlKEV2ZW50X1RpbWVfSHIgPCAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMF8yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEV2ZW50X1RpbWVfSHIgPCA2LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwM181IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEV2ZW50X1RpbWVfSHIgPCA5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwNl84IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEV2ZW50X1RpbWVfSHIgPCAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDlfMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDE1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMTJfMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDE4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMTVfMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMThfMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDI0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMjFfMjMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKSkpKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJHcm91cDBfMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwM181IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXA2XzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDlfMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDEyXzE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxNV8xNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMThfMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDIxXzIzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICApCgpybShBbGxEYXlzX0ZpcnN0U3RvcElEKQpzdHIoQWxsRGF5c19BZGRWYXJzKQoKCiMgZ3JvdXBfYnkoQWxsRGF5c19BZGRWYXJzLAojICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAojICAgICAgICAgKSAlPiUgCiMgICBzdW1tYXJpc2UoQ250cyA9IG4oKQojICAgICAgICAgICAgKQoKCiMgVmlldyhoZWFkKGZpbHRlcihBbGxEYXlzX0FkZFZhcnMsCiMgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyID09IDAKIyAgICAgICAgICAgICAgICAgKSwKIyAgICAgICAgICAgNTAKIyAgICAgICAgICApCiMgICAgICkKCiMgVmlldyhoZWFkKEFsbERheXNfQWRkVmFycywgNTApKQoKYGBgCgoKPCEtLSBGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgdGhlIGRpc3RhbmNlIHRyYXZlbGVkIGJhc2VkIG9uIHRoZSBIYXZlcnNpbmUgZm9ybXVsYS4gIE9yaWdpbmFsIGNvZGUgZnJvbTogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vZ3JlYXQtY2lyY2xlLWRpc3RhbmNlLWNhbGN1bGF0aW9ucy1pbi1yLyAtLT4KPCEtLSBgYGB7cn0gLS0+Cgo8IS0tIGdjZC5oZiA8LSBmdW5jdGlvbihsb25nMSwgbGF0MSwgbG9uZzIsIGxhdDIpIHsgLS0+CjwhLS0gICBSIDwtIDYzNzEgIyBFYXJ0aCBtZWFuIHJhZGl1cyBba21dIC0tPgo8IS0tICAgZGVsdGEubG9uZyA8LSAobG9uZzIgLSBsb25nMSkgLS0+CjwhLS0gICBkZWx0YS5sYXQgPC0gKGxhdDIgLSBsYXQxKSAtLT4KPCEtLSAgIGEgPC0gc2luKGRlbHRhLmxhdC8yKV4yICsgY29zKGxhdDEpICogY29zKGxhdDIpICogc2luKGRlbHRhLmxvbmcvMileMiAtLT4KPCEtLSAgIGMgPC0gMiAqIGFzaW4obWluKDEsc3FydChhKSkpIC0tPgo8IS0tICAgZCA9IFIgKiBjICogMC42MjEzNzEgIyAxIGttID0gMC42MjEzNzEgbWlsZXMgLS0+CjwhLS0gICByZXR1cm4oZCkgIyBEaXN0YW5jZSBpbiBtaWxlcyAtLT4KPCEtLSB9IC0tPgoKPCEtLSBgYGAgLS0+CgoKRmVhdHVyZSBlbmdpbmVlcmluZy4KCkNyZWF0aW5nIG1vcmUgdmFyaWFibGVzLiBDcmVhdGluZyBhIEJ1c0V2ZW50IHJvdyBudW1iZXIgZm9yIGZ1dHVyZSBpZGVudGlmaWNhdGlvbiBwdXJwb3Nlcy4gVGhlbiwgY3JlYXRpbmcgdmFyaW91cyB2YXJpYWJsZXMgdG8gYW5hbHl6ZSBkaXN0YW5jZSB0cmF2ZWxlZCBhbmQgc3BlZWQuCmBgYHtyfQoKQWxsRGF5c19CdXNEYXkgPC0gZ3JvdXBfYnkoQWxsRGF5c19BZGRWYXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShCdXNEYXlfRXZlbnROdW0gPSByb3dfbnVtYmVyKCksICAjIHVzZWQgdG8gaWRlbnRpZnkgQnVzIG1vdmVtZW50cyBvbiBhIHBhcnRpY3VsYXIgZGF0ZQogICAgICAgICAKICAgICAgICAgUm91dGVfTGFnMSA9IGxhZyhSb3V0ZSksICAjIHVzZWQgaW4gZnV0dXJlIGFuYWx5c2VzIHRvIGlkZW50aWZ5IFJvdXRlIGNoYW5nZXMKICAgICAgICAgUm91dGVBbHRfTGFnMSA9IGxhZyhSb3V0ZUFsdCksICAjIHVzZWQgaW4gZnV0dXJlIGFuYWx5c2VzIHRvIGlkZW50aWZ5IFJvdXRlQWx0IChkaXJlY3Rpb24pIGNoYW5nZXMKICAgICAgICAgCiAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEgPSBsYWcoT2RvbWV0ZXJfRGlzdGFuY2UpLAogICAgICAgICAKICAgICAgICAgTGF0aXR1ZGVfTDEgPSBsYWcoTGF0aXR1ZGUpLAogICAgICAgICBMb25naXR1ZGVfTDEgPSBsYWcoTG9uZ2l0dWRlKSwKICAgICAgICAgIyBMYXRfUmFkaWFuID0gTGF0aXR1ZGUqcGkvMTgwLAogICAgICAgICAjIExvbmdfUmFkaWFuID0gTG9uZ2l0dWRlKnBpLzE4MCwKICAgICAgICAgIyBMYXRfUmFkaWFuX0wxID0gbGFnKExhdF9SYWRpYW4pLAogICAgICAgICAjIExvbmdfUmFkaWFuX0wxID0gbGFnKExvbmdfUmFkaWFuKSwKICAgICAgICAgCiAgICAgICAgICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIGRpc3RhbmNlcwogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9GdCA9IGlmZWxzZShPZG9tZXRlcl9EaXN0YW5jZSA+IE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlIC0gT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA9IFRyYXZlbERpc3RhbmNlX0Z0IC8gNTI4MCwgIzUsMjgwIGZlZXQgaW4gMSBtaWxlCiAgICAgICAgIAogICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pMiA9IGdjZC5oZihsb25nMSA9IExvbmdfUmFkaWFuX0wxLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXQxID0gTGF0X1JhZGlhbl9MMSwKICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9uZzIgPSBMb25nX1JhZGlhbiwKICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0MiA9IExhdF9SYWRpYW4KICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycyA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGlmZWxzZSgoaXMubmEoTG9uZ2l0dWRlX0wxKSB8IGlzLm5hKExhdGl0dWRlX0wxKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdEhhdmVyc2luZShjYmluZChMb25naXR1ZGVfTDEsIExhdGl0dWRlX0wxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYmluZChMb25naXR1ZGUsIExhdGl0dWRlKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAqIDAuMDAwNjIxMzcxLCAjIDAuMDAwNjIxMzcxIG1pbGVzID0gMSBtZXRlcgogICAgICAgICAKICAgICAgICAgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgdGltZXMKICAgICAgICAgVHJhdmVsVGltZV9TZWMgPSBhcy5udW1lcmljKGlmZWxzZShFdmVudF9UaW1lID4gbGFnKERlcGFydHVyZV9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lIC0gbGFnKERlcGFydHVyZV9UaW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUcmF2ZWxUaW1lX0hyID0gVHJhdmVsVGltZV9TZWMgLyAzNjAwLCAjIDMsNjAwIHNlY29uZHMgaW4gMSBob3VyCiAgICAgICAgIAogICAgICAgICAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSBvciB6ZXJvIHRyYXZlbCB0aW1lcwogICAgICAgICBTcGVlZEF2Z19NcGggPSBpZmVsc2UoVHJhdmVsVGltZV9IciA+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSAvIFRyYXZlbFRpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAKICAgICAgICAgU3RhcnRfSUQgPSBsYWcoU3RvcElEX0NsZWFuKSwKICAgICAgICAgU3RhcnRfRGVzYyA9IGxhZyhTdG9wX0Rlc2MpLAogICAgICAgICBTdGFydFN0b3BfSUQgPSBpZmVsc2UoaXMubmEoU3RhcnRfSUQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIk5VTEwiLCBTdG9wSURfQ2xlYW4sIHNlcCA9ICItLSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoU3RhcnRfSUQsIFN0b3BJRF9DbGVhbiwgc2VwID0gIi0tIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKCnJtKEFsbERheXNfQWRkVmFycykKc3RyKEFsbERheXNfQnVzRGF5KQoKIyBzdW1tYXJ5KEFsbERheXNfQnVzRGF5KQoKIyBWaWV3KHRhaWwoQWxsRGF5c19CdXNEYXksIDUwKSkKCmBgYAoKCkluc3BlY3RpbmcgZm9yIGlzc3VlcyB3aXRoIFN0YXJ0U3RvcF9JRCAod2hlcmUgdGhlIHZhbHVlIGlzIGVpdGhlciBOQSBvciBjb250YWlucyBOVUxMKS4gVGhleSBPTkxZIGV4aXN0IHdoZW4gQnVzRGF5X0V2ZW50TnVtID0gMSAod2hpY2ggaXMgYnkgZGVzaWduKS4gU28gZXZlcnl0aGluZyBsb29rcyBPSy4KYGBge3J9CgpWaWV3KGdyb3VwX2J5KEFsbERheXNfQnVzRGF5LAogICAgICAgICAgICAgIFN0YXJ0U3RvcF9JRAogICAgICAgICAgICAgKSAlPiUgCiAgICAgICBzdW1tYXJpc2UoCiAgICAgICAgIENudCA9IG4oKQogICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250KQogICAgICAgICAgICAgICkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfQnVzRGF5LAogICAgICAgICAgICAoaXMubmEoU3RhcnRTdG9wX0lEKSB8CiAgICAgICAgICAgICAgc3RyX2RldGVjdChTdGFydFN0b3BfSUQsICJOVUxMIikKICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEKICAgICAgICAgICApCiAgICApCgpgYGAKCgpTdGF0cyAocXVhbnRpbGVzKSBvdmVyYWxsIGZvciBUcmF2ZWxEaXN0YW5jZV9NaS4KYGBge3J9CgpRdWFudGlsZXNfZHQgPC0gQWxsRGF5c19CdXNEYXkgJT4lIAogIG11dGF0ZShURF9NaV9xMiA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjAyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9xOTggPSBxdWFudGlsZSh4ID0gVHJhdmVsRGlzdGFuY2VfTWksIHByb2JzID0gMC45OCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX3EyID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfU2VjLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19xOTggPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC45OCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfcTIgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjAyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9xOTggPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSAlPiUgCiAgZGF0YS50YWJsZSgpCgoKU3RhdHMgPC0gUXVhbnRpbGVzX2R0ICU+JSAKICBtdXRhdGUoVERfTWlfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX01lYW5fRiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfcTIgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9xOThdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9NZWRfRiA9IG1lZGlhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9xMiA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX0NudCA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9xMiA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX3E5OF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIAogICAgICAgICBUVF9TZWNfTWVhbiA9IG1lYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19xMiA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19xOThdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfTWVkID0gbWVkaWFuKFRyYXZlbFRpbWVfU2VjLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfU2VjX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19xMiA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19xOThdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICApLAoKICAgICAgICAgVFRfSHJfTWVhbiA9IG1lYW4oVHJhdmVsVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX3EyIDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9IcltUVF9Icl9xMiA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9xOThdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9DbnQgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHIpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX0hyW1RUX0hyX3EyIDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX3E5OF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApICU+JSAKICBkYXRhLmZyYW1lKCkKCnJtKEFsbERheXNfQnVzRGF5KQpybShRdWFudGlsZXNfZHQpCnN0cihTdGF0cykKIyBWaWV3KGhlYWQoU3RhdHMsIDUwKSkKCmBgYAoKClN0YXRzIGZvciBTdGFydFN0b3BfSUQuCmBgYHtyfQoKUXVhbnRpbGVzX1NTX2R0IDwtIGdyb3VwX2J5KFN0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjA1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9TU19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsRGlzdGFuY2VfTWksIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfU2VjLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NfcTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjA1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSAlPiUgCiAgZGF0YS50YWJsZSgpCgoKU3RhdHNfU3RTdCA8LSBncm91cF9ieShRdWFudGlsZXNfU1NfZHQsCiAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVERfTWlfU1NfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX01lYW5fRiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfU1NfcTUgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9TU19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTX0NudCA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIAogICAgICAgICBUVF9TZWNfU1NfTWVhbiA9IG1lYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NfTWVkID0gbWVkaWFuKFRyYXZlbFRpbWVfU2VjLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfU2VjX1NTX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWMpKSwKICAgICAgICAgVFRfU2VjX1NTX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAoKICAgICAgICAgVFRfSHJfU1NfTWVhbiA9IG1lYW4oVHJhdmVsVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9TU19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9IcltUVF9Icl9TU19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9TU19DbnQgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHIpKSwKICAgICAgICAgVFRfSHJfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGRhdGEuZnJhbWUoKQoKcm0oU3RhdHMpCnJtKFF1YW50aWxlc19TU19kdCkKc3RyKFN0YXRzX1N0U3QpCiMgVmlldyhoZWFkKFN0YXRzX1N0U3QsIDUwKSkKCmBgYAoKClN0YXRzIGZvciBTdGFydFN0b3BfSUQgd2l0aCBFdmVudF9UaW1lX0hyR3JvdXAuCmBgYHtyfQoKUXVhbnRpbGVzX1NTSEdfZHQgPC0gZ3JvdXBfYnkoU3RhdHNfU3RTdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVERfTWlfU1NIR19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjA1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9TU0hHX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NIR19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjA1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NIR19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NIR19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfcTk1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfSHIsIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKQogICAgICAgICkgJT4lIAogIGRhdGEudGFibGUoKQoKClN0YXRzX1N0U3RfSHJHcnAgPC0gZ3JvdXBfYnkoUXVhbnRpbGVzX1NTSEdfZHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFREX01pX1NTSEdfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU0hHX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTSEdfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBURF9NaV9TU0hHX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTSEdfcTUgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU0hHX0NudCA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NIR19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfU1NIR19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTSEdfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIAogICAgICAgICBUVF9TZWNfU1NIR19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTSEdfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NIR19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX01lZF9GID0gbWVkaWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU0hHX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfU2VjX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykpLAogICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NIR19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAoKICAgICAgICAgVFRfSHJfU1NIR19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU0hHX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9IcltUVF9Icl9TU0hHX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9TU0hHX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU0hHX01lZF9GID0gbWVkaWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9Icl9TU0hHX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikpLAogICAgICAgICBUVF9Icl9TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NIR19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGRhdGEuZnJhbWUoKQoKcm0oU3RhdHNfU3RTdCkKcm0oUXVhbnRpbGVzX1NTSEdfZHQpCnN0cihTdGF0c19TdFN0X0hyR3JwKQojIFZpZXcoaGVhZChTdGF0c19TdFN0X0hyR3JwLCA1MCkpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgYSBCdXNFdmVudFJvdXRlIHJvdyBudW1iZXIsIGFuZCBhIFJvdXRlQWx0X0xhZzEgaW5kaWNhdG9yIGZvciBmdXR1cmUgaWRlbnRpZmljYXRpb24gcHVycG9zZXMuIApgYGB7cn0KCiMgcm0oUXVhbnRpbGVzX2R0KQojIHJtKFF1YW50aWxlc19TU19kdCkKIyBybShBbGxEYXlzX0J1c0RheSkKIyBybShRdWFudGlsZXNfU1NIR19kdCkKIyBybShTdGF0c19TdFN0KQoKIyBBbGxEYXlzX0J1c0RheVJvdXRlIDwtIGdyb3VwX2J5KFN0YXRzX1N0U3RfSHJHcnAsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKIyAgIG11dGF0ZShSb3V0ZUFsdF9MYWcyID0gbGFnKFJvdXRlQWx0KSAgIyB1c2VkIGluIGZ1dHVyZSBhbmFseXNlcyB0byBpZGVudGlmeSBSb3V0ZUFsdCAoZGlyZWN0aW9uKSBjaGFuZ2VzCiMgICAgICAgICAgCiMgICAgICAgICAgIyBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxID0gbGFnKE9kb21ldGVyX0Rpc3RhbmNlKSwKIyAgICAgICAgICAjIAojICAgICAgICAgICMgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgZGlzdGFuY2VzCiMgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9GdCA9IGlmZWxzZShPZG9tZXRlcl9EaXN0YW5jZSA+PSBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2UgLSBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKIyAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pID0gVHJhdmVsRGlzdGFuY2VfRnQgLyA1MjgwLCAjNSwyODAgZmVldCBpbiAxIG1pbGUKIyAgICAgICAgICAjIAojICAgICAgICAgICMgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgdGltZXMKIyAgICAgICAgICAjIFRyYXZlbFRpbWVfU2VjID0gYXMubnVtZXJpYyhpZmVsc2UoRXZlbnRfVGltZSA+PSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lIC0gbGFnKERlcGFydHVyZV9UaW1lKSwKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgVHJhdmVsVGltZV9IciA9IFRyYXZlbFRpbWVfU2VjIC8gMzYwMCwgIyAzLDYwMCBzZWNvbmRzIGluIDEgaG91cgojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSBvciB6ZXJvIHRyYXZlbCB0aW1lcwojICAgICAgICAgICMgU3BlZWRBdmdfTXBoID0gaWZlbHNlKFRyYXZlbFRpbWVfSHIgPiAwLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pIC8gVHJhdmVsVGltZV9IciwKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgKSAlPiUgCiMgICBkYXRhLmZyYW1lKCkKIyAKIyBybShTdGF0c19TdFN0X0hyR3JwKQojIHN0cihBbGxEYXlzX0J1c0RheVJvdXRlKQoKYGBgCgoKRmVhdHVyZSBlbmdpbmVlcmluZy4KCkNhbGN1bGF0aW5nIGEgdmFyaWFibGUgdG8ga25vdyBpZiB0aGUgUm91dGVBbHQgY2hhbmdlZC4gQ291bGQgYmUgdXNlZnVsIGluIGhlbHBpbmcgaWRlbnRpZnlpbmcgd2VpcmRuZXNzIGluIGNhbGN1bGF0ZWQgZGlzdGFuY2VzIGFuZCBzcGVlZHMuCmBgYHtyfQoKIyBybShTdGF0c19TdFN0X0hyR3JwKQoKQWxsRGF5c19EaXJDaGFuZ2UgPC0gU3RhdHNfU3RTdF9IckdycCAlPiUgICMgQWxsRGF5c19CdXNEYXlSb3V0ZSAlPiUgCiAgbXV0YXRlKFJ0ZUNoYW5nZSA9IGlmZWxzZShSb3V0ZSA9PSBSb3V0ZV9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgUnRlQ2hhbmdlMiA9IGZhY3RvcihpZmVsc2UoaXMubmEoUnRlQ2hhbmdlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIERpckNoYW5nZSA9IGlmZWxzZShSb3V0ZUFsdCA9PSBSb3V0ZUFsdF9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgRGlyQ2hhbmdlMiA9IGZhY3RvcihpZmVsc2UoaXMubmEoRGlyQ2hhbmdlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgojIHJtKEFsbERheXNfQnVzRGF5Um91dGUpCnJtKFN0YXRzX1N0U3RfSHJHcnApCnN0cihBbGxEYXlzX0RpckNoYW5nZSkKClZpZXcoZmlsdGVyKEFsbERheXNfRGlyQ2hhbmdlLAogICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMjU3MDA2MCwgMjU3MDA4MCkKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApCiAgICApCgpgYGAKCgpSZS1vcmRlcmluZyB0aGUgdmFyaWFibGVzIHRvIGVhc2Ugd2l0aCBjb21wcmVoZW5zaW9uLgpgYGB7cn0KCkFsbERheXNfTmV3T3JkZXIgPC0gIHNlbGVjdChBbGxEYXlzX0RpckNoYW5nZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvd051bV9PRywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVuaXF1ZUxhdExuZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVBbHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFJvdXRlQWx0X0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVfRGlyZWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9TZXF1ZW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXJ0X0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRfRGVzYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3RvcF9JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0Rlc2MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3VudHJ5Q29kZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfU3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0NvdW50eSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ2l0eSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X0Rlc2NyaXB0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9ZciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVwYXJ0dXJlX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEd2VsbF9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRHdlbGxfVGltZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgTGF0aXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBIZWFkaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTWksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9GdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX3EyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfcTk4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19xMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19xOTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX0hyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfcTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9xOTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNwZWVkQXZnX01waAogICAgICAgICAgICAgICAgICAgICAgICAgICApCgpybShBbGxEYXlzX0RpckNoYW5nZSkKc3RyKHNlbGVjdChBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQpzdHIoQWxsRGF5c19OZXdPcmRlcikKCiMgVmlldyhoZWFkKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCiMgVmlldyh0YWlsKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCgpgYGAKCgpTdW1tYXJpemluZyB0aGUgZGF0YSB0byBoZWxwIHNwb3QgYW5vbW9saWVzLgpgYGB7cn0KClZpZXcoZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICBTdG9wX0NpdHkpICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAogICAgICAgICAgICAgICAgIENudF9QY3QgPSAxMDAqQ250X051bSAvIChucm93KEFsbERheXNfTmV3T3JkZXIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250X051bSkpCikKCnN1bW1hcnkoQWxsRGF5c19OZXdPcmRlcikKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpWaWV3KFRyYXZEaXN0TWlfUGN0aWxlcyk6IDk5JSBvZiBUcmF2ZWxEaXN0YW5jZV9NaSBhcmUgYWJvdXQgMSBtaWxlIG9yIGxlc3MuLi5idXQgc29tZSB3ZWlyZCBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgKGUuZy4sIDU4NCBtaWxlcyB0cmF2ZWxlZCkgZXhpc3QuCmBgYHtyfQoKVHJhdkRpc3RNaV9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3T3JkZXIkVHJhdmVsRGlzdGFuY2VfTWkpICU+JSAKICBtdXRhdGUoI1BjdGlsZSA9IG50aWxlKEFsbERheXNfTmV3T3JkZXIkVHJhdmVsRGlzdGFuY2VfTWksIDEwMCksCiAgICAgICAgICNNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhUcmF2RGlzdE1pX050aWxlKVsxXSA8LSAiVHJhdmVsRGlzdGFuY2VfTWkiCiMgc3RyKFRyYXZEaXN0TWlfTnRpbGUpCgpUcmF2RGlzdE1pX050aWxlX1Jvd3MgPC0gbnJvdyhUcmF2RGlzdE1pX050aWxlKQoKIyBWaWV3KHRhaWwoVHJhdkRpc3RNaV9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pX1BjdGlsZXMgPC0gZ3JvdXBfYnkoVHJhdkRpc3RNaV9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVHJhdkRpc3RNaUF0UGN0aWxlID0gbWluKFRyYXZlbERpc3RhbmNlX01pKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZEaXN0TWlfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpybShUcmF2RGlzdE1pX050aWxlKQpybShUcmF2RGlzdE1pX050aWxlX1Jvd3MpCgpWaWV3KFRyYXZEaXN0TWlfUGN0aWxlcykKVHJhdkRpc3RNaV9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKV2h5IGFyZSBzb21lIFRyYXZlbERpc3RhbmNlX01pICJOQSI/IEl0IGxvb2tzIGxpa2UgcGFydGlhbGx5IGJlY2F1c2UgdGhlIHJlY29yZHMgYXJlIHRoZSBmaXJzdCB0cmlwIG9mIHRoZSBkYXkgKGZvciB0aGF0IGJ1cyksIHNvIEkgcHVycG9zZWZ1bGx5IHNldCB0aGUgZGlzdGFuY2UgdG8gIk5BIi4gQW5vdGhlciByZWFzb24gaXMgZHVlIHRvIHRoZSBvZG9tZXRlciByZWNvcmRpbmcgYSB2YWx1ZSBsZXNzIHRoYW4gdGhlIHByZXZpb3VzIG9kb21ldGVyIHJlY29yZGluZy4gSW4gbW9zdCBjYXNlcywgSSBoYXZlIG5vIGV4cGxhbmF0aW9uIGZvciB0aGlzIC0gdGhvdWdoIEkgaGF2ZSBvYnNlcnZlZCBhYm91dCA2NyUgb2YgYWxsIGluc3RhbmNlcyB3aGVyZSBUcmF2ZWxEaXN0YW5jZV9NaSBpcyBOQSAob3RoZXIgdGhhbiBiZWNhdXNlIGl0J3MgdGhlIGZpcnN0IHJlY29yZCBvZiB0aGUgZGF5KSBhcmUgaW5zdGFuY2VzIHdoZXJlIERpckNoYW5nZTIgaXMgIkNoYW5nZSIuIFRoaXMgaXMgd2VpcmQgYW5kIHNob3VsZCBiZSBhc2tlZCB0byBXTUFUQS4KYGBge3J9CgojIFZpZXcoaGVhZChBbGxEYXlzX05ld09yZGVyLCA1MDApKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgIyBXaGVuIEJ1c0RheV9FdmVudE51bSA9PSAxLCBUcmF2ZWxEaXN0YW5jZV9NaSBpcyBOQSBieSBkZXNpZ24gKGRvbid0IHdhbnQgdG8gY2FsY3VsYXRlIGRpc3RhbmNlIGJhc2VkIG9uIHllc3RlcmRheSdzIHBvc2l0aW9uKQogICAgICAgICAgICkgJT4lIAogICAgICAgZ3JvdXBfYnkoU3RhcnRTdG9wX0lEKSAlPiUgCiAgICAgICBzdW1tYXJpc2UoQ250cyA9IHN1bShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250cykKICAgICAgICAgICAgICApCiAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjEwMDAyNDUtLTEwMDAyMTEiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoUm93TnVtX09HLAogICAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCwKICAgICAgICAgICAgICBFdmVudF9UaW1lLAogICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCwKICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgIFREX01pX1NTX01lYW5fRiwKICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW4sCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVkLAogICAgICAgICAgICAgIFREX01pX1NTX01lZF9GLAogICAgICAgICAgICAgIFREX01pX1NTSEdfTWVkLAogICAgICAgICAgICAgIFREX01pX1NTSEdfTWVkX0YsCiAgICAgICAgICAgICAgVERfTWlfU1NfQ250LAogICAgICAgICAgICAgIFREX01pX1NTX0NudF9GLAogICAgICAgICAgICAgIFREX01pX1NTSEdfQ250LAogICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YKICAgICAgICAgICAgICApICU+JSAKICAgICAgIG11dGF0ZShSYXRpb19NZWFuVG9IdnJzID0gVERfTWlfU1NfTWVhbiAvIFRyYXZlbERpc3RhbmNlX01pX0h2cnMpICU+JSAKICAgICAgIGFycmFuZ2UoRXZlbnRfVGltZSkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIGlzLm5hKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICkKICAgICkKCiMgVGhlc2UgcmVjb3JkcyBhcmUgTkEgYmVjdWFzZSB0aGUgcmVjb3JkIGlzIHRoZSBmaXJzdCByZWNvcmQgb2YgdGhlIGRheSAodGhlIEV2ZW50X1RpbWVfRGF0ZSkKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDMyNiwgMzQ2KSB8ICMgMzM2CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDU5MSwgNjExKSB8ICMgNjAxCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDg0NSwgODY1KSAjIDg1NQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpUaGVzZSByZWNvcmRzIGFyZSBOQSBiZWN1YXNlIHRoZSBjdXJyZW50IHJlY29yZCBvZG9tZXRlciBpcyBsZXNzIHRoYW4gdGhlIHByZXZpb3VzIHJlY29yZCBvZG9tZXRlci4gVGhlb3JldGljYWxseSwgdGhpcyBzaG91bGQgTk9UIGhhcHBlbi4gTWU6IGl0IGFwcGVhcnMgdGhhdCBhYm91dCA2NyUgb2YgYWxsIGluc3RhbmNlcyB3aGVyZSBUcmF2ZWxEaXN0YW5jZV9NaSBpcyBOQSAob3RoZXIgdGhhbiBiZWNhdXNlIGl0J3MgdGggZmlyc3QgcmVjb3JkIG9mIHRoZSBkYXkpIGFyZSBpbnN0YW5jZXMgd2hlcmUgRGlyQ2hhbmdlMiBpcyAiQ2hhbmdlIi4gVGhpcyBpcyB3ZWlyZCBhbmQgc2hvdWxkIGJlIGFza2VkIHRvIFdNQVRBLgpgYGB7cn0KClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxOTQsIDIxNCkgfCAjIDIwNAogICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0NDAsIDQ2MCkgfCAjIDQ1MAogICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0NzgsIDQ5OCkgfCAjIDQ4OAogICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA1MTAsIDUzMCkgIyA1MjAKICAgICAgICAgICApCiAgICApCgpUZXN0VGFibGUgPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEKICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShUcmF2ZWxEaXN0YW5jZV9OQSA9IGFzLmZhY3RvcihpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZhbHNlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lCiAgZ3JvdXBfYnkoRGlyQ2hhbmdlMiwgVHJhdmVsRGlzdGFuY2VfTkEpICU+JQogIHN1bW1hcmlzZShUcmF2RGlzdE1pX05BQ250cyA9IG4oKQogICAgICAgICAgICkKCiMgVGVzdFRhYmxlCgpUZXN0VGFibGVfU3ByZWFkIDwtIGFzLmRhdGEuZnJhbWUoc3ByZWFkKFRlc3RUYWJsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9OQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2RGlzdE1pX05BQ250cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoRmFsc2UsCiAgICAgICAgIFRydWUKICAgICAgICApCgpyb3cubmFtZXMoVGVzdFRhYmxlX1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihUZXN0VGFibGVfU3ByZWFkKQojIFRlc3RUYWJsZV9TcHJlYWQKCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRlc3RUYWJsZV9TcHJlYWQpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIDEKICAgICAgICAgICkKCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRlc3RUYWJsZV9TcHJlYWQpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIDIKICAgICAgICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpMZXQncyBsb29rIGF0IGp1c3QgdGhlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyB0aGF0IGFyZSBOT1QgIk5BIi4KYGBge3J9CgpybShUZXN0VGFibGUsIFRlc3RUYWJsZV9TcHJlYWQpCgpUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BIDwtIGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKZGltKEFsbERheXNfTmV3T3JkZXIpCmRpbShUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQpucm93KEFsbERheXNfTmV3T3JkZXIpIC0gbnJvdyhUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQoKc3RyKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCnN1bW1hcnkoVHJhdmVsRGlzdGFuY2VfTWlfTm9OQSkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpMZXQncyBwbG90IGp1c3QgdGhlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyB0aGF0IGFyZSBOT1QgIk5BIi4KYGBge3J9CgpUcmF2RGlzdE1pX0hpc3REZW4gPC0gZ2dwbG90KHNlbGVjdChUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjA1LCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEuNSksIHlsaW0gPSBjKDAsIDQuMCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBEaXN0YW5jZSBCZXR3ZWVuIFN0b3BzIiwKICAgICAgIHggPSAiVHJhdmVsIERpc3RhbmNlIChtaWxlcykiLAogICAgICAgeSA9ICJEZW5zaXR5IgogICAgICApCgpUcmF2RGlzdE1pX0hpc3REZW4KCmBgYAoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxvb2tpbmcgYXQgdGhlIGV4dHJlbWVseSBsYXJnZSBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMuIFNvbWUgKGFwcm94IDI3JSkgb2YgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzID4gMSBtaWxlIGFyZSB3aGVuIHRoZSBEaXJDaGFuZ2UyIGNoYW5nZXMuLi5idXQgd2hhdCBhYm91dCB0aGUgb3RoZXIgfjczJT8KYGBge3J9CgpybShUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQoKIyBleGFtcGxlcyBvZiB3ZWlyZGx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gMS4xNTg3MTIxMjEyICMgMS4xNTg3MTIxMjEyIGlzIHRoZSA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICApCiAgICApCgoKIyBXaHkgYXJlIHRoZXNlIGV4dHJlbWVzPyAgQWlycG9ydHM/ICBCdXMgY29sbGVjdGlvbiBwb2ludHM/ClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDA0NCwgNDk0MDY0KSB8ICMgNDk0MDU0CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDYyNiwgNDk0NjQ2KSB8ICMgNDk0NjM2CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDE2MTAxNTYsIDE2MTAxNzYpIHwgIyAxNjEwMTY2CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAogICAgICAgICAgICkKICAgICkKCiMgQmVmb3JlIFJlbW92aW5nIFJ1bnMKIyBWaWV3KGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKIyAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MDQ0LCA0OTQwNjQpIHwgIyA0OTQwNTQKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQyNzMsIDQ5NDI5MykgfCAjIDQ5NDI4MwojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDYyNiwgNDk0NjQ2KSB8ICMgNDk0NjM2CiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMTYxMDE1NiwgMTYxMDE3NikgfCAjIDE2MTAxNjYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAyMDczMDc0LCAyMDczMDk0KSAjIDIwNzMwODQKIyAgICAgICAgICAgICkKIyAgICAgKQoKIyBBZnRlciBSZW1vdmluZyBSdW5zCiMgVmlldyhmaWx0ZXIoQWxsRGF5c19GaXJzdFN0b3BJRCwKIyAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MDQ0LCA0OTQwNjQpIHwgIyA0OTQwNTQKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQyNzMsIDQ5NDI5MykgfCAjIDQ5NDI4MwojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDYyNiwgNDk0NjQ2KSB8ICMgNDk0NjM2CiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMTYxMDE1NiwgMTYxMDE3NikgfCAjIDE2MTAxNjYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAyMDczMDc0LCAyMDczMDk0KSAjIDIwNzMwODQKIyAgICAgICAgICAgICkKIyAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkFueSByZWxhdGlvbiB3aXRoIERpckNoYW5nZTI/ICBEb2Vzbid0IGxvb2sgYXMgaWYgdGhpcyBpcyBzby4KYGBge3J9CgpFeHRyZW1lVHJhdkRpc3QgPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRyYXZEaXN0X0V4dHJlbWUgPSBpZmVsc2UoVHJhdmVsRGlzdGFuY2VfTWkgPiAxLjE1ODcxMjEyMTIsICMgMS4xNTg3MTIxMjEyIGlzIHRoZSA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZhbHNlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KERpckNoYW5nZTIsIFRyYXZEaXN0X0V4dHJlbWUpICU+JSAKICBzdW1tYXJpc2UoVHJhdkRpc3RNSV9FeHRDbnRzID0gbigpCiAgICAgICAgICAgKQoKIyBFeHRyZW1lVHJhdkRpc3QKCgpFeHRyZW1lVHJhdkRpc3RfU3ByZWFkIDwtIGFzLmRhdGEuZnJhbWUoc3ByZWFkKEV4dHJlbWVUcmF2RGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2RGlzdF9FeHRyZW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0TUlfRXh0Q250cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoRmFsc2UsCiAgICAgICAgIFRydWUKICAgICAgICApCgpyb3cubmFtZXMoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihFeHRyZW1lVHJhdkRpc3RfU3ByZWFkKQojIEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQKCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIDEKICAgICAgICAgICkKCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIDIKICAgICAgICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpMb29raW5nIGF0IHNwZWNpZmljIGJ1c2VzIGFuZCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKcm0oRXh0cmVtZVRyYXZEaXN0LCBFeHRyZW1lVHJhdkRpc3RfU3ByZWFkKQoKVmlldyhhcnJhbmdlKGdyb3VwX2J5KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQKICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICAgICAgICAgIHN1bW1hcmlzZShEaXN0VHJhdl9NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RUcmF2X01lZCA9IG1lZGlhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgZGVzYyhEaXN0VHJhdl9NZWQpCiAgICAgICAgICAgICkKICAgICkKCgojIGV4YW1wbGUgb2YgZXh0cmVtZWx5IHNtYWxsIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyAobG9va3MgbGlrZSB0aGUgb2RvbWV0ZXIgd2Fzbid0IGZ1bmN0aW9uaW5nKQpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBCdXNfSUQgPT0gNjExMSB8CiAgICAgICAgICAgICAgQnVzX0lEID09IDcyMDEgfAogICAgICAgICAgICAgIEJ1c19JRCA9PSA4MDU4CiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKEJ1c19JRCwgRXZlbnRfVGltZSkKICAgICkKCgpWaWV3KGFycmFuZ2UoZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIFN0YXJ0U3RvcF9JRAogICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgc3VtbWFyaXNlKERpc3RUcmF2X01lYW4gPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgRGlzdFRyYXZfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICBkZXNjKERpc3RUcmF2X01lZCkKICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlIG9mIGV4dHJlbWVseSBsYXJnZSBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMuLi5ubyBpZGVhIHdoeS4uLgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjEwMDM2NjUtLTEyIiB8CiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lEID09ICIxMDAzNjY1LS01MDAxOTI1IiB8CiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lEID09ICIzMDAxMDM4LS0zMDAyNTY1IgogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShTdGFydFN0b3BfSUQsIEV2ZW50X1RpbWUpCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKSWYgVHJhdmVsRGlzbnRhY2VfTWkgaXMgYmVsb3cgdGhlIDV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgb3IgaWYgVHJhdmVsRGlzbnRhY2VfTWkgaXMgYWJvdmUgdGhlIDk1dGggcGVyY2VudGlsZSBmb3IgdGhhdCBTdGFydFN0b3BfSUQsIG9yIGlmIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BICh3aGVuIHRoZSBCdXNEYXlfRXZlbnROdW0gIT0xKSwgY29uc2lkZXIgdGhpcyBhbiBvdXRsaWVyLiAgSW4gdGhpcyBjYXNlLCByZXBsYWNlIHRoZSB2YWx1ZSB3aXRoIHRoZSBtZWFuIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCBhbmQgSG91ckdyb3VwIChURF9NaV9TU0hHX01lYW5fRiksIG9yIGlmIHRoZXJlIGFyZSBub3QgZW5vdWdoIHZhbHVlcyBhdCB0aGUgSG91ckdyb3VwIGxldmVsLCByZXBsYWNlIGl0IHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lELgpgYGB7cn0KCiMgVmlldyh0YWlsKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCgpBbGxEYXlzX05ld1RyYXZlbERpc3QgPC0gCiAgbXV0YXRlKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA9IGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnQgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzID09IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSksCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCA9IAogICAgICAgICAgIGZhY3RvcihpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlREX01pX1NTSEdfTWVhbl9GIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWkgPCBURF9NaV9TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiBURF9NaV9TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVERfTWlfU1NfTWVhbl9GIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWkgPCBURF9NaV9TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiBURF9NaV9TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycyAhPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRyYXZlbERpc3RhbmNlX01pX0h2cnMiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzID09IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVERfTWlfU1NfTWVhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsRGlzdGFuY2VfTWkiCiAgICAgICAgICAgICAgICAgICAgICAgICkpKSkpCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyA9IGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycyAhPSAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPCBURF9NaV9xMiB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPiBURF9NaV9xOTgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnNfTGFiZWwgPQogICAgICAgICAgIGZhY3RvcihpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX0h2cnMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycyAhPSAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pX05ldyA8IFREX01pX3EyIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID4gVERfTWlfcTk4CiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsRGlzdGFuY2VfTWlfSHZycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoVHJhdmVsRGlzdGFuY2VfTWlfTmV3X0xhYmVsKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMgPSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIC8gVHJhdmVsVGltZV9IcgogICAgICAgICkKCnN0cihBbGxEYXlzX05ld1RyYXZlbERpc3QpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfSHZycyAmIFRyYXZlbERpc3RhbmNlX01pX05ldy4KClF1aWNrIHN1bW1hcnkgYW5kIHRoZW4gY29ycmVsYXRpb24gY2FsY3VsYXRpb24uCmBgYHtyfQoKcm0oQWxsRGF5c19OZXdPcmRlcikKCgojIDM4IHJvd3MgbWVldCB0aGlzIGNyaXRlcmlhIGFueW1vcmUgIC0tICBhcHBlYXJzIHRvIGJlIHRoZSBjYXNlIHdoZW4gYm90aCB0aGUgTGF0IExvbmcgY2FsY3VsYXRpb25zLCBhbmQgdGhlIFRyYXZlbERpc3RhbmNlIGNhbGN1bGF0aW9ucyBkaWQgbm90IGZ1bmN0aW9uIHByb3Blcmx5LgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIGlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ldykgJgogICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgKQogICAgKQoKVmlldyhBbGxEYXlzX05ld1RyYXZlbERpc3QgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pX05ldykpICU+JSAKICAgICAgIGhlYWQoNTAwKQogICAgKQoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKY29yKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgKSwKICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiCiAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbC4KClNob3cgaG93IHRoZSBsYWJlbHMgY2hhbmdlZC4KYGBge3J9Cgpncm91cF9ieShBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbAogICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShDbnROdW0gPSBuKCksCiAgICAgICAgICAgIENudFBjdCA9IGZvcm1hdChDbnROdW0gLyBucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gOTk5OQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKENudFBjdCkKICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpHcmFwaGluZyB0aGUgdHdvIG1ldGhvZHMgb2YgY2FsY3VsYXRpbmcgVHJhdmVsRGlzdGFuY2VfTWkuCgpGaXJzdCwgbGV0J3MgZ2V0IGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGxpbmVyIG1vZGVsIGVxdWF0aW9uLgpgYGB7cn0KCmxtX2VxbiA8LSBmdW5jdGlvbihkZiwgeSwgeCl7CiAgbSA8LSBsbSh5IH4geCwgZGYpCiAgCiAgbCA8LSBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVsxXSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIGIgPSBmb3JtYXQoYWJzKGNvZWYobSlbMl0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgczEgPSBpZmVsc2UodGVzdCA9IGNvZWYobSlbMl0gPiAwLAogICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiKyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gIi0iCiAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDMKICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKQogIAogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGF+fnMxfn5iICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsCiAgICAgICAgICAgICAgICAgICBsCiAgICAgICAgICAgICAgICAgICkKICAKICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkKICAgICAgICAgICAgICApICAgICAgICAgICAgIAp9CgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycy4KClNjYXR0ZXIgcGxvdCAodXNpbmcgYSAxMCUgc2FtcGxlIHRvIG1ha2luZyBwbG90dGluZyB0aW1lIGZhc3RlciBhbmQgdG8gcmVkdWNlIHVuLW5lZWRlZCBkYXRhIGluIHRoZSAic2FtZSIgc3Bsb3QpLgpgYGB7cn0KCnNldC5zZWVkKDEyMzQ1Njc4OSkKQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0IDwtIGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHJlbmFtZShEaXN0TWV0aG9kID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkKCgpUcmF2RGlzdF9NaVZzQ2FsYyA8LSBnZ3Bsb3Qoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIiwgImdyZWVuIiwgIm9yYW5nZSIsICJibGFjayIpCiAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3NoYXBlKHNvbGlkID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvdXIgPSAiYmx1ZSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEuNSksIHlsaW0gPSBjKDAsIDEuNSkKICAgICAgICAgICAgICAgICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgI2MoMC44NSwgMC40MCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpCiAgICAgICApICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0JFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAjIHggPSA2MiwKICAgICAgICAgICAjIHkgPSAyMCwKICAgICAgICAgICB4ID0gMC43MCwKICAgICAgICAgICB5ID0gMC4wMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gIlJlZmVyZW5jZSBMaW5lIChzbG9wZSA9IDEpIiwKICAgICAgICAgICAjIHggPSAxNiwKICAgICAgICAgICAjIHkgPSAzMCwKICAgICAgICAgICB4ID0gMC44MCwKICAgICAgICAgICB5ID0gMS4wNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJyZWQiCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlRyYXZlbERpc3RhbmNlX01pIHZzLiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIiwKICAgICAgIHggPSAiVHJhdmVsRGlzdGFuY2VfTWkiLAogICAgICAgeSA9ICJUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIgogICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKVHJhdkRpc3RfTWlWc0NhbGMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKR3JhcGhpbmcgdGVzdCB3aXRoIHJib2tlaC4KYGBge3J9CgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaCA8LSBmaWd1cmUoZGF0YSA9IHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0TWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsIDEuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gYygwLCAxLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xvY2F0aW9uID0gImJvdHRvbV9yaWdodCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbHlfcG9pbnRzKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgeSA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgIGNvbG9yID0gRGlzdE1ldGhvZCwKICAgICAgICAgICAgaG92ZXIgPSBjKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsIFRyYXZlbERpc3RhbmNlX01pLCBEaXN0TWV0aG9kKQogICAgICAgICAgICkgJT4lIAogIGx5X2FibGluZShhID0gMCwgYiA9IDEsIGNvbG9yID0gInJlZCIpCgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpDYWxjdWxhdGluZyB0aGUgbWluaW11bSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KCnJtKFRyYXZEaXN0X01pVnNDYWxjX0Jva2VoKQpybShBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QpCgoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdkRpc3RNaU5fTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFBjdFJfTiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICAgICAgIyBQY3RSX0ggPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgICAgICBQY3RSX1JvdW5kX04gPSByb3VuZChQY3RSX04sIDIpCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pTl9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaU5fTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pTl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaU5fTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pTl9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pTl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlOX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfTgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVERNaUF0UGN0aWxlX04gPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICMgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICBDbnRzQXRQY3RpbGVfTiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSksCiAgICAjIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICBQY3RzQXRQY3RpbGVfTiA9IENudHNBdFBjdGlsZV9OIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICAgIyBQY3RzQXRQY3RpbGVfSCA9IENudHNBdFBjdGlsZV9IIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pCiAgICAgICAgICMgQ3VtU3VtUEF0UF9IID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9IKQogICAgICAgICkKCiMgVmlldyhUcmF2RGlzdE1pTl9QY3RpbGVzKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCgpDYWxjdWxhdGluZyB0aGUgbWluaW11bSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIHZhbHVlIGF0IGVhY2ggcGVyY2VudGlsZS4KYGBge3J9CgpUcmF2RGlzdE1pSF9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdfTGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnNfTGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKCMgUGN0Ul9OID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpLAogICAgICAgICBQY3RSX0ggPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgICAgICAjIFBjdFJfUm91bmRfTiA9IHJvdW5kKFBjdFJfTiwgMiksCiAgICAgICAgIFBjdFJfUm91bmRfSCA9IHJvdW5kKFBjdFJfSCwgMikKICAgICAgICApIAoKIyBzdHIoVHJhdkRpc3RNaUhfTnRpbGUpCiMgVmlldyhoZWFkKFRyYXZEaXN0TWlIX050aWxlLCA1MDApKQoKVHJhdkRpc3RNaUhfTnRpbGVfUm93cyA8LSBucm93KFRyYXZEaXN0TWlIX050aWxlKQoKIyBWaWV3KHRhaWwoVHJhdkRpc3RNaUhfTnRpbGUsIDUwMCkpCgoKVHJhdkRpc3RNaUhfUGN0aWxlcyA8LSBncm91cF9ieShUcmF2RGlzdE1pSF9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kX0gKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgICMgTWluVERNaUF0UGN0aWxlX04gPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgIE1pblRETWlBdFBjdGlsZV9IID0gbWluKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgIyBDbnRzQXRQY3RpbGVfTiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSksCiAgICBDbnRzQXRQY3RpbGVfSCA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycykpLAogICAgIyBQY3RzQXRQY3RpbGVfTiA9IENudHNBdFBjdGlsZV9OIC8gVHJhdkRpc3RNaUhfTnRpbGVfUm93cywKICAgIFBjdHNBdFBjdGlsZV9IID0gQ250c0F0UGN0aWxlX0ggLyBUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKCMgQ3VtU3VtUEF0UF9OID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9OKSwKICAgICAgICAgQ3VtU3VtUEF0UF9IID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9IKQogICAgICAgICkKCiMgVmlldyhUcmF2RGlzdE1pSF9QY3RpbGVzKQoKYGBgCgoKSm9pbiBUcmF2RGlzdE1pSF9QY3RpbGVzLCBUcmF2RGlzdE1pTl9QY3RpbGVzLCBhbmQgVHJhdkRpc3RNaV9QY3RpbGVzLgoKfjExJSBvZiByaWRlcyBhcmUgc3RpbGwgc2hvd2luZyBhcyBsZXNzIHRoYW4gMC4xIG1pbGVzIG9mIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMuCmBgYHtyfQoKcm0oVHJhdkRpc3RNaU5fTnRpbGVfUm93cywgVHJhdkRpc3RNaUhfTnRpbGVfUm93cywgVHJhdkRpc3RNaU5fTnRpbGUsIFRyYXZEaXN0TWlIX050aWxlKQoKCiMgVmlldyhUcmF2RGlzdE1pX1BjdGlsZXMpCiMgVmlldyhUcmF2RGlzdE1pTl9QY3RpbGVzKQojIFZpZXcoVHJhdkRpc3RNaUhfUGN0aWxlcykKClRyYXZEaXN0TWlfUGN0aWxlc19BbGwgPC0gaW5uZXJfam9pbih4ID0gVHJhdkRpc3RNaV9QY3RpbGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFRyYXZEaXN0TWlOX1BjdGlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlBjdFJfUm91bmQiID0gIlBjdFJfUm91bmRfTiIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGlubmVyX2pvaW4oeSA9IFRyYXZEaXN0TWlIX1BjdGlsZXMsCiAgICAgICAgICAgICBieSA9IGMoIlBjdFJfUm91bmQiID0gIlBjdFJfUm91bmRfSCIpCiAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChQY3RSX1JvdW5kLAogICAgICAgICBNaW5UcmF2RGlzdE1pQXRQY3RpbGUsCiAgICAgICAgIE1pblRETWlBdFBjdGlsZV9OLAogICAgICAgICBNaW5URE1pQXRQY3RpbGVfSCwKICAgICAgICAgQ250c0F0UGN0aWxlLAogICAgICAgICBDbnRzQXRQY3RpbGVfTiwKICAgICAgICAgQ250c0F0UGN0aWxlX0gsCiAgICAgICAgIFBjdHNBdFBjdGlsZSwKICAgICAgICAgUGN0c0F0UGN0aWxlX04sCiAgICAgICAgIFBjdHNBdFBjdGlsZV9ILAogICAgICAgICBDdW1TdW1QQXRQLAogICAgICAgICBDdW1TdW1QQXRQX04sCiAgICAgICAgIEN1bVN1bVBBdFBfSAogICAgICAgICApCgojIHN0cihUcmF2RGlzdE1pX1BjdGlsZXNfQWxsKQoKcm0oVHJhdkRpc3RNaV9QY3RpbGVzLCBUcmF2RGlzdE1pTl9QY3RpbGVzLFRyYXZEaXN0TWlIX1BjdGlsZXMpCgoKVmlldyhUcmF2RGlzdE1pX1BjdGlsZXNfQWxsKQpUcmF2RGlzdE1pX1BjdGlsZXNfQWxsCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pX05ldy4KCldoeSBhcmUgdGhlcmUgc3RpbGwgc29tZSBzbWFsbCBvciBsYXJnZSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIHZhbHVlcy4KYGBge3J9CgojIFZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKIyAgICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycykKIyAgICAgICAgICAgICkgJT4lIAojICAgICAgICBzZWxlY3QoLVREX01pX3EyLAojICAgICAgICAgICAgICAgLVREX01pX3E5OCwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19xNSwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19xOTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19xNSwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU0hHX3E5NSwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWFuLAojICAgICAgICAgICAgICAgLVREX01pX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWFuLAojICAgICAgICAgICAgICAgLVREX01pX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU0hHX01lYW4sCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfTWVkLAojICAgICAgICAgICAgICAgLVREX01pX01lZF9GLAojICAgICAgICAgICAgICAgLVREX01pX1NTX01lZCwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU0hHX01lZCwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU0hHX01lZF9GLAojICAgICAgICAgICAgICAgLVREX01pX0NudCwKIyAgICAgICAgICAgICAgIC1URF9NaV9DbnRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19DbnQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19DbnQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfcTIsCiMgICAgICAgICAgICAgICAtVFRfU2VjX3E5OCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfcTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX3E5NSwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19xNSwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19xOTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfU2VjX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfTWVhbiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfTWVhbl9GLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfTWVhbl9GLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19NZWQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfQ250LAojICAgICAgICAgICAgICAgLVRUX1NlY19DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfQ250LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19DbnQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfcTIsCiMgICAgICAgICAgICAgICAtVFRfSHJfcTk4LAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX3E1LAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX3E5NSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX3E1LAojICAgICAgICAgICAgICAgLVRUX0hyX1NTSEdfcTk1LAojICAgICAgICAgICAgICAgLVRUX0hyX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfSHJfTWVhbl9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NfTWVhbl9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTSEdfTWVhbiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9NZWQsCiMgICAgICAgICAgICAgICAtVFRfSHJfTWVkX0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NfTWVkLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTSEdfTWVkLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTSEdfTWVkX0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfQ250LAojICAgICAgICAgICAgICAgLVRUX0hyX0NudF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX0NudF9GCiMgICAgICAgICAgICAgICkgJT4lIAojICAgICAgICBhcnJhbmdlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICU+JSAKIyAgICAgICAgaGVhZCg1MDApCiMgICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgojIGV4YW1wbGVzIG9mIHRoZSBzbWFsbGVzdCBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIHZhbHVlcy4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDE0MjQ0NDAgJiBSb3dOdW1fT0cgPD0gMTQyNDQ2MCkgfCAjIDE0MjQ0NTAgIC0tICBkaXJlY3Rpb24gY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDc2MzI5MiAmIFJvd051bV9PRyA8PSA3NjMzMTIpIHwgIyA3NjMzMDIgIC0tICBkaXJlY3Rpb24gY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDE2NzkwOTMgJiBSb3dOdW1fT0cgPD0gMTY3OTExMykgfCAjIDE2NzkxMDMgIC0tICBkaXJlY3Rpb24gY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI4NjA5MTggJiBSb3dOdW1fT0cgPD0gMjg2MDkzOCkgIyAyODYwOTI4ICAtLSAgbG9va3MgY29ycmVjdAogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyBvZiB0aGUgbGFyZ2VzdCBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIHZhbHVlcy4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDEwOTIwMDAgJiBSb3dOdW1fT0cgPD0gMTA5MjA1MCkgfCAjIDEwOTIwMzAgIC0tICBkaXJlY3Rpb24gY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDE2MDk0NjAgJiBSb3dOdW1fT0cgPD0gMTYwOTQ4MCkgfCAjIDE2MDk0NzAgIC0tIGRpcmVjdGlvbiBjaGFuZ2UgCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDUwODkwNCAmIFJvd051bV9PRyA8PSA1MDg5MjQpIHwgIyA1MDg5MTQgIC0tICBkaXJlY3Rpb24gY2hhbmdlICYgb3JpZ2luYWwgU3RvcElEIHdhcyBiYWQKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjQ3NjM0NSAmIFJvd051bV9PRyA8PSAyNDc2MzY1KSAjIDI0NzYzNTUgIC0tICBkaXJlY3Rpb24gY2hhbmdlCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxUaW1lX0hyLgoKVmlldyhUcmF2RGlzdE1pX1BjdGlsZXMpOiA5OCUgb2YgVHJhdmVsVGltZV9IciBhcmUgYmV0d2VlbiA3IHNlY29uZHMgYW5kIDQ2NCBzZWNvbmRzICh+OCBtaW51dGVzKS4KYGBge3J9CgpUcmF2VGltZUhyX050aWxlIDwtIHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfSHIKICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoIyBQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsVGltZV9IciwgMTAwKSwKICAgICAgICAgIyBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbFRpbWVfSHIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX0hyKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCiMgc3RyKFRyYXZUaW1lSHJfTnRpbGUpCgpUcmF2VGltZUhyX050aWxlX1Jvd3MgPC0gbnJvdyhUcmF2VGltZUhyX050aWxlKQoKIyBWaWV3KHRhaWwoVHJhdlRpbWVIcl9OdGlsZSwgNTAwKSkKCgpUcmF2VGltZUhyX1BjdGlsZXMgPC0gZ3JvdXBfYnkoVHJhdlRpbWVIcl9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVHJhdlRpbWVIckF0UGN0aWxlID0gbWluKFRyYXZlbFRpbWVfSHIpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gVHJhdlRpbWVIcl9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKSwKICAgICAgICAgTWluVHJhdlRpbWVTZWNBdFBjdGlsZSA9IE1pblRyYXZUaW1lSHJBdFBjdGlsZSAqIDM2MDAKICAgICAgICApCgpybShUcmF2VGltZUhyX050aWxlX1Jvd3MpCnJtKFRyYXZUaW1lSHJfTnRpbGUpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQpUcmF2VGltZUhyX1BjdGlsZXMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9Ici4KCkhpc3RvZ3JhbSBvZiBUcmF2ZWxUaW1lX1NlYy4KYGBge3J9CgpUcmF2VGltZV9TZWNfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICAjIHN0YXRfYmluKGJpbndpZHRoID0gNSwKICAjICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgIyAgICAgICAgICBzaXplID0gMi41LAogICMgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgIyAgICAgICAgICBhZXMobGFiZWwgPSBmb3JtYXQoLi5jb3VudC4uLCBiaWcubWFyayA9ICIsIikKICAjICAgICAgICAgICAgICksCiAgIyAgICAgICAgICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxODApLCB5bGltID0gYygwLCAwLjAyKQogICAgICAgICAgICAgICAgICkgKwogICMgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBUcmF2ZWwgVGltZSIsCiAgICAgICB4ID0gIlRyYXZlbCBUaW1lIChzZWMpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKVHJhdlRpbWVfU2VjX0hpc3REZW4KCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIE5BLgpgYGB7cn0KCnN1bW1hcnkoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbFRpbWVfU2VjKQoKClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKGlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAgIyBUcmF2ZWxUaW1lIHB1cnBvc2VmdWxseSBub3QgY2FsY3VsYXRlZCBoZXJlCiAgICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGVzIG9mIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyB0aGF0IGFyZSBOQS4gVGhlc2UgYXJlIE5BIGJlY2F1c2UgdGhlIEV2ZW50X1RpbWUgJiBEZXBhcnR1cmVfVGltZSByZWFkaW5ncyBhcmUgbm90IGFjY3VyYXRlIChpLmUuLCB0aGUgcHJldmlvdXMgRGVwYXJ0dXJlX1RpbWUgaXMgQkVGT1JFIG9yIEVRVUFMIFRPIHRoZSBjdXJyZW50IEV2ZW50X1RpbWUpLgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gOTA4MDkgJiBSb3dOdW1fT0cgPD0gOTA4MjkpIHwgIyA5MDgxOQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA5MDg4MSAmIFJvd051bV9PRyA8PSA5MDkwMSkgfCAjIDkwODkxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI1OTcwNjYgJiBSb3dOdW1fT0cgPD0gMjU5NzA4NikgfCAjIDI1OTcwNzYKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjYxMzMwNSAmIFJvd051bV9PRyA8PSAyNjEzMzI1KSAjIDI2MTMzMTUKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxUaW1lX1NlYy4KClRyYXZlbFRpbWVfU2VjIHZhbHVlcyBhcmUgZXh0cmVtZWx5IHNtYWxsLgpgYGB7cn0KClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgZGVzYyhTcGVlZEF2Z19NcGhfTmV3SHZycykKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFRyYXZlbFRpbWVfU2VjIGlzIHNtYWxsICgxIHNlYykgYW5kIFNwZWVkQXZnX01waF9OZXdIdnJzIGlzIGxhcmdlLgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcigoUm93TnVtX09HID49IDIyMTczNTMgJiBSb3dOdW1fT0cgPD0gMjIxNzM3MykgfCAjIDIyMTczNjMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzA5MDMyMSAmIFJvd051bV9PRyA8PSAzMDkwMzQxKSB8ICMgMzA5MDMzMQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4MDc2NCAmIFJvd051bV9PRyA8PSA4MDc4NCkgfCAjIDgwNzc0CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMzODQwICYgUm93TnVtX09HIDw9IDMzODYwKSAjIDMzODUwCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxUaW1lX1NlYy4KClRyYXZlbFRpbWVfU2VjIHZhbHVlcyBhcmUgZXh0cmVtZWx5IGxhcmdlLgpgYGB7cn0KClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbFRpbWVfU2VjKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFRyYXZlbFRpbWVfU2VjIGlzIGxhcmdlIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBzbWFsbC4KVmlldyhzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBmaWx0ZXIoKFJvd051bV9PRyA+PSAxMDA3NzAzICYgUm93TnVtX09HIDw9IDEwMDc3MjMpIHwgIyAxMDA3NzEzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDIzNzM1NjQgJiBSb3dOdW1fT0cgPD0gMjM3MzU4NCkgfCAjIDIzNzM1NzQKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODY0Mzc5ICYgUm93TnVtX09HIDw9IDg2NDM5OSkgfCAjIDg2NDM4OQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNTcwMDYwICYgUm93TnVtX09HIDw9IDI1NzAwODApICMgMjU3MDA3MAogICAgICAgICAgICkKICAgICkKCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LiBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZSAob24gdGhlIGhpZ2ggc2lkZSkuCmBgYHtyfQoKVFRMYXJnZVJ0ZUNobmcgPC0gc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRUX091dCA9IGZhY3RvcihpZmVsc2UoVHJhdmVsVGltZV9TZWMgPiA0NjQsICAjIHRoaXMgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdXRsaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVFRMYXJnZVJ0ZUNobmcpCgoKVFRMYXJnZVJ0ZUNobmdfQ250cyA8LSBncm91cF9ieShUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKENudHMgPSBuKCkKICAgICAgICAgICApCgpUVExhcmdlUnRlQ2huZ19TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVFRMYXJnZVJ0ZUNobmdfQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENudHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lCiAgc2VsZWN0KC1SdGVDaGFuZ2UyKQoKcm93Lm5hbWVzKFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihUVExhcmdlUnRlQ2huZ19TcHJlYWQpCgoKIyBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZS4KVFRMYXJnZVJ0ZUNobmdfU3ByZWFkCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVFRMYXJnZVJ0ZUNobmdfU3ByZWFkKQogICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAyCiAgICAgICAgICApCgojIHJtKFRUTGFyZ2VSdGVDaG5nLCBUVExhcmdlUnRlQ2huZ19TcHJlYWQpCiAgICAgICAgIAoKVmlldyhmaWx0ZXIoVFRMYXJnZVJ0ZUNobmcsCiAgICAgICAgICAgICFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgIFJ0ZUNoYW5nZTIgPT0gIlNhbWUiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGhfTmV3SHZycwogICAgICAgICAgICAgICkgJT4lCiAgICAgICBoZWFkKDUwMCkKICAgICkKCgojIGV4YW1wbGVzIHdoZXJlIFRyYXZlbFRpbWVfU2VjIGlzIGxhcmdlIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBzbWFsbC4KVmlldyhmaWx0ZXIoVFRMYXJnZVJ0ZUNobmcsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjI1MDI5MCAmIFJvd051bV9PRyA8PSAyMjUwMzEwKSB8ICMgMjI1MDMwMAogICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODY3NzE3ICYgUm93TnVtX09HIDw9IDg2NzczNykgfCAjIDg2NzcyNwogICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODY0Mzc5ICYgUm93TnVtX09HIDw9IDg2NDM5OSkgfCAjIDg2NDM4OQogICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODA4Mzk1ICYgUm93TnVtX09HIDw9IDgwODQxNSkgIyA4MDg0MDUKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKSWYgVHJhdmVsVGltZV9TZWMgaXMgYmVsb3cgdGhlIDV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgb3IgaWYgVHJhdmVsVGltZV9TZWMgaXMgYWJvdmUgdGhlIDk1dGggcGVyY2VudGlsZSBmb3IgdGhhdCBTdGFydFN0b3BfSUQsICBjb25zaWRlciB0aGlzIGFuIG91dGxpZXIuICBJbiB0aGlzIGNhc2UsIHJlcGxhY2UgdGhlIHZhbHVlIHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lEIGFuZCBIb3VyR3JvdXAgKFRUX1NlY19TU0hHX01lYW5fRiksIG9yIGlmIHRoZXJlIGFyZSBub3QgZW5vdWdoIHZhbHVlcyBhdCB0aGUgSG91ckdyb3VwIGxldmVsLCByZXBsYWNlIGl0IHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lELgpgYGB7cn0KCnJtKFRUTGFyZ2VSdGVDaG5nLCBUVExhcmdlUnRlQ2huZ19DbnRzLCBUVExhcmdlUnRlQ2huZ19TcHJlYWQpCgoKTmV3VHJhdlRpbWUgPC0gbXV0YXRlKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19OZXcgPSBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250IDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIgPT0gIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKSksCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19OZXdfTGFiZWwgPSAKICAgICAgICAgICBmYWN0b3IoaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJUVF9TZWNfU1NIR19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVFRfU2VjX1NTX01lYW5fRiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJUVF9TZWNfU1NfTWVhbiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250IDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyID09ICJDaGFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsVGltZV9TZWMiCiAgICAgICAgICAgICAgICAgICAgICAgICkpKSkKICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgVFRfSHJfTmV3ID0gVFRfU2VjX05ldyAvICg2MCAqIDYwKQogICAgICAgICAgICkKCgpkaW0oQWxsRGF5c19OZXdUcmF2ZWxEaXN0KQpkaW0oTmV3VHJhdlRpbWUpCnJtKEFsbERheXNfTmV3VHJhdmVsRGlzdCkKCnN1bW1hcnkoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICBUVF9TZWNfTmV3LAogICAgICAgICAgIFRUX1NlY19OZXdfTGFiZWwsCiAgICAgICAgICAgVFRfSHJfTmV3CiAgICAgICAgICApCiAgICkKCgpzdW1tYXJ5KHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgIFRUX1NlY19OZXcsCiAgICAgICAgICAgICAgIFRUX1NlY19OZXdfTGFiZWwsCiAgICAgICAgICAgICAgIFRUX0hyX05ldwogICAgICAgICAgICAgICkKICAgICAgICkKCmBgYAoKClRlc3QgaW52ZXN0aWdhdGlvbiBvZiBqdXN0IHRoZSBYMiBSb3V0ZS4gQm94IHBsb3RzIGZvciB0aW1lIGJldHdlZW4gYnVzIGFycml2YWxzIChieSBIb3VyR3JvdXApLgpgYGB7cn0KClZpZXcoaGVhZChzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICAgICAgKQogICAgICAgICApCiAgICApCgpYMiA8LSBzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICkgJT4lIAogIGZpbHRlcihSb3V0ZSA9PSAiWDIiKQoKc3RyKFgyKQoKVmlldyhoZWFkKGFycmFuZ2UoWDIsCiAgICAgICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKWDJfQnlTdG9wIDwtIGdyb3VwX2J5KFgyLAogICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZSkgJT4lIAogIG11dGF0ZShFdmVudF9UaW1lX0wxID0gbGFnKEV2ZW50X1RpbWUpLAogICAgICAgICBUaW1lVG9FdmVudF9TZWMgPSBhcy5udW1lcmljKEV2ZW50X1RpbWUgLSBFdmVudF9UaW1lX0wxKSwKICAgICAgICAgVGltZVRvRXZlbnRfTWluID0gVGltZVRvRXZlbnRfU2VjIC8gNjAKICAgICAgICApCgpWaWV3KGhlYWQoWDJfQnlTdG9wLCA1MDApKQoKCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKQ291bnRfVmFsdWVzIDwtIGRkcGx5KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihUaW1lVG9FdmVudF9NaW4sIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9Cb3hQbG90IDwtIGdncGxvdChzZWxlY3QoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG5vdGNoPVRSVUUsIG5hLnJtID0gVFJVRSkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDEyMCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIkhvdyBPZnRlbiBhbiBYMiBBcnJpdmVzIGF0IGEgR2l2ZW4gU3RvcCIsCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJUaW1lIEJldHdlZW4gQnVzc2VzIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3QKCmBgYAoKClRlc3QgaW52ZXN0aWdhdGlvbiBvZiBqdXN0IHRoZSBYMiBSb3V0ZS4gVmlvbGluIHBsb3RzIGZvciB0aW1lIGJldHdlZW4gYnVzIGFycml2YWxzIChieSBIb3VyIEdyb3VwKS4KYGBge3J9CgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3QgPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlcywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDgwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBCb3ggcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzCkNvdW50X1ZhbHVlc196IDwtIGRkcGx5KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgLihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFRpbWVUb0V2ZW50X01pbiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfQm94UGxvdF96IDwtIGdncGxvdChzZWxlY3QoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX1ppcAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmYWN0b3IoU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlc196LAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDEwMCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIkhvdyBPZnRlbiBhbiBYMiBBcnJpdmVzIGF0IGEgR2l2ZW4gU3RvcCIsCiAgICAgICB4ID0gIlppcCBDb2RlIG9mIERlc3RpbmF0aW9uIiwKICAgICAgIHkgPSAiVGltZSBCZXR3ZWVuIEJ1c3NlcyAobWluKSIKICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9Cb3hQbG90X3oKCmBgYAoKClRlc3QgaW52ZXN0aWdhdGlvbiBvZiBqdXN0IHRoZSBYMiBSb3V0ZS4gVmlvbGluIHBsb3RzIGZvciB0aW1lIGJldHdlZW4gYnVzIGFycml2YWxzIChieSBaaXAgQ29kZSkuCmBgYHtyfQoKVGltZUJ0d0V2ZW50c19YMl9WaW9saW5QbG90X3ogPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmYWN0b3IoU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUgPSAiY291bnQiLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IE5BLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gVFJVRQogICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudF9WYWx1ZXNfeiwKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDYwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJUaW1lIEJldHdlZW4gQnVzc2VzIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3RfegoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpGaXJzdCwgZ2V0IHRoZSBtYXggYW5kIG1pbiB0aW1lcyBvZiBidXMgc3RvcHMgKGVhY2ggZGF5LCBhbmQgZm9yIGVhY2ggcm91dGUpLgpgYGB7cn0KClJvdXRlTWluTWF4IDwtIGdyb3VwX2J5KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKE1pblRpbWUgPSBtaW4oRXZlbnRfVGltZSksCiAgICAgICAgICAgIE1heFRpbWUgPSBtYXgoRXZlbnRfVGltZSkKICAgICAgICAgICApCgpzdHIoUm91dGVNaW5NYXgpClZpZXcoUm91dGVNaW5NYXgpCgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCihQdWxscyBoZXJlIGFyZSBkb25lIGJ5IGRheSwgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMzo3KXsKCnNldC5zZWVkKDEyMzQ1Njc4OSkKU2FtcCA8LSBzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgIFJvd051bV9PRywKICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICMgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFdmVudCIpCiAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKEV2ZW50X1RpbWVfRGF0ZSA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBkYXkgKDMtNykgYmVjYXVzZSB0aGUgY29tcGxldGUgZmlsZSB3YXMgdG9vIGxhcmdlIHRvIGRvIGF0IG9uY2UKICBsZWZ0X2pvaW4oUm91dGVNaW5NYXgsCiAgICAgICAgICAgIGJ5ID0gYygiUm91dGUiID0gIlJvdXRlIiwKICAgICAgICAgICAgICAgICAgICJFdmVudF9UaW1lX0RhdGUiID0gIkV2ZW50X1RpbWVfRGF0ZSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShTYW1wVGltZSA9IGFzX2RhdGV0aW1lKHJ1bmlmKG5yb3coLiksICMyMDAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSBNYXhUaW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAKCiMgc3RyKFNhbXApCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCiMgCiMgVmlldygKIyBncm91cF9ieShTYW1wLAojICAgICAgICAgIFJvd051bV9PRwojICAgICAgICAgKSAlPiUKIyAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAojICAgICAgICAgICAgIENudF9QY3QgPSAxMDAgKiBDbnRfTnVtIC8gbnJvdyhTYW1wKQojICAgICAgICAgICAgKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKIyApCgoKIyBGb3IgZWFjaCBSb3V0ZSBhbmQgU3RvcElEIGNvbWJpbmF0aW9uLCBnZXQgYWxsIHRoZSBFdmVudF9UaW1lIHZhbHVlcyB0aGF0IGFyZSBhZnRlciB0aGUgU2FtcFRpbWUgdmFsdWUuCiMgZXN0aW1hdGluZyBhcHByb3ggMmhycyBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmdfQSA8LSBzcWxkZigiICAgU2VsZWN0ICAgICAgICAgICAgICAgdDEuKgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lICAgICAgICAgICAgIGFzIE5leHRCdXMKICAgICAgICAgICAgICAgICAgICAgICAgRnJvbSAgICAgICAgICAgICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIElubmVyIEpvaW4gICAgICBTYW1wICAgICAgICAgICAgICAgICAgICAgIGFzIHQyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT24gICAgICAgICAgICAgIHQxLlJvdXRlID0gdDIuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbmQgICAgICAgICAgICAgdDEuU3RvcElEX0NsZWFuID0gdDIuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQyLkV2ZW50X1RpbWUgPiB0MS5TYW1wVGltZQogICAgICAgICAgICAgICAgICAgICAgICBPcmRlciBCeSAgICAgICAgICAgICB0MS5Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0MS5TdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgICIKICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoTkIgPSBhc19kYXRldGltZShOZXh0QnVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVGVzdGluZ19BKQojIFZpZXcoaGVhZChUZXN0aW5nX0EsIDUwMCkpCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCgoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSB0byBvbmx5IGluY2x1ZGUgdGhlIGJ1cyBhcnJpdmFsIGF0IFN0b3BJRCB0aGF0IGlzIHRoZSBuZXh0IHRvIGNvbWUgYWZ0ZXIgdGhlIFNhbXBUaW1lLgojIGVzdGltYXRpbmcgYXBwcm94IDIwbWluIG9mIHJ1bnRpbWUgZm9yIGFsbCAyLjhtIHJlY29yZHMKVGVzdGluZyA8LSBzZWxlY3QoVGVzdGluZ19BLAogICAgICAgICAgICAgICAgICAtTmV4dEJ1cwogICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KFJvd051bV9PRykgJT4lIAogIGZpbHRlcihOQiA9PSBtaW4oTkIpCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFdhaXRUaW1lX01pbiA9IGFzLm51bWVyaWMoTkIgLSBTYW1wVGltZSksCiAgICAgICAgIFdhaXRUaW1lX1NlYyA9IFdhaXRUaW1lX01pbiAqIDYwLAogICAgICAgICBXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZ18iLCBpKSwKICAgICAgIFRlc3RpbmcKICAgICAgKQoKcm0oU2FtcCxUZXN0aW5nX0EsIFRlc3RpbmcpCnN0cihnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKVmlldyhnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfRGF5UHVsbCA8LSBiaW5kX3Jvd3MoVGVzdGluZ18zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RpbmdfNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZ182LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfU2VjMyA9IE5CIC0gU2FtcFRpbWUsCiAgICAgICAgIFdhaXRUaW1lX01pbjMgPSBXYWl0VGltZV9TZWMzIC8gNjAKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApCgpybShUZXN0aW5nXzMsIFRlc3RpbmdfNCwgVGVzdGluZ181LCBUZXN0aW5nXzYsIFRlc3RpbmdfNykKc3RyKFdhaXREYXRhX0RheVB1bGwpClZpZXcoaGVhZChXYWl0RGF0YV9EYXlQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfRGF5UHVsbCwgNTAwKSkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQmFzaWMgaW52ZXN0aWdhdGlvbiBvZiBhbnkgbWlzc2luZyByb3dzIGZyb20gZGF0YSBwdWxsZWQgYnkgZGF5LgpgYGB7cn0KCkRpc3RpbmN0Um93TnVtX09HIDwtIGRpc3RpbmN0KHNlbGVjdChXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoRGlzdGluY3RSb3dOdW1fT0cpCgpWaWV3KAphbnRpX2pvaW4oU2FtcCwKICAgICAgICAgIERpc3RpbmN0Um93TnVtX09HLAogICAgICAgICAgYnkgPSBjKCJSb3dOdW1fT0ciID0gIlJvd051bV9PRyIpCiAgICAgICAgICkKKQoKCiMgVGhlIHNhbXAgdGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuClZpZXcoZmlsdGVyKFNhbXAsCiAgICAgICAgICAgIEV2ZW50X1RpbWUgPiAiMjAxNi0xMC0wNyAxOTo0ODo0MSIgJgogICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAzNzc0CiAgICAgICAgICAgKQogICAgKQoKIyBOZXh0IEJ1cyAoTkIpIGNhbiBiZSBvbiB0aGUgbmV4dCBtb3JuaW5nClZpZXcoZmlsdGVyKFRlc3Rpbmc3LAogICAgICAgICAgICBTYW1wVGltZSA+ICIyMDE2LTEwLTA2IDIzOjU4OjAwIiAmCiAgICAgICAgICAgICAgU2FtcFRpbWUgPCAiMjAxNi0xMC0wNiAyMzo1OTo1OSIpCiAgICApCgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCihQdWxscyBoZXJlIGFyZSBkb25lIGJ5IGdyb3VwaW5ncyBvZiBidXMgcm91dGVzLCBhcyB0aGUgZGF0YSBhcmUgdG9vIGxhcmdlIHRvIGRvIGF0IG9uY2UuKQoKRmlyc3QsIHdlIG5lZWQgdG8gZmluZCB0aGUgbW9zdCBjb21tb24gYnVzIHJvdXRlcy4KYGBge3J9CgpybShEaXN0aW5jdFJvd051bV9PRykKCgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCnNldC5zZWVkKDEyMzQ1Njc4OSkKQnVzR3JvdXBzIDwtIGdyb3VwX2J5KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgUm91dGUKICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKENudF9OdW0gPSBuKCksCiAgICAgICAgICAgIENudF9QY3QgPSBDbnRfTnVtIC8gbnJvdyhOZXdUcmF2VGltZSkKICAgICAgICAgICApICU+JSAKICBhcnJhbmdlKGRlc2MoQ250X051bSkKICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFJvd051bSA9IHJvd19udW1iZXIoKSwKICAgICAgICAgUmFuZE51bSA9IHJ1bmlmKG4gPSAyNjgpLAogICAgICAgICBSb3V0ZUdyb3VwID0gaWZlbHNlKFJhbmROdW0gPD0gMC4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUmFuZE51bSA8PSAwLjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShSYW5kTnVtIDw9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFJhbmROdW0gPD0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKQogICAgICAgICkKCnN0cihCdXNHcm91cHMpClZpZXcoQnVzR3JvdXBzKQpzdW1tYXJ5KEJ1c0dyb3VwcykKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKKFB1bGxzIGhlcmUgYXJlIGRvbmUgYnkgZ3JvdXBpbmdzIG9mIGJ1cyByb3V0ZXMsIGFzIHRoZSBkYXRhIGFyZSB0b28gbGFyZ2UgdG8gZG8gYXQgb25jZS4pCmBgYHtyfQoKIyBWaWV3KGhlYWQoTmV3VHJhdlRpbWUsIDUwMCkpCgojIEZvciBlYWNoIHJlY29yZCwgY3JlYXRlIGEgcmFuZG9tIGRhdGV0aW1lIGJldHdlZW4gdGhlIGZpcnN0IGFuZCBsYXN0IHN0b3AgZm9yIHRoYXQgYnVzIHJvdXRlIChvbiB0aGF0IGRheSkuCmZvcihpIGluIDE6NSl7CiAgCnNldC5zZWVkKDEyMzQ1Njc4OSkKU2FtcCA8LSBsZWZ0X2pvaW4oTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgICAgIEJ1c0dyb3VwcywKICAgICAgICAgICAgICAgICAgYnkgPSBjKCJSb3V0ZSIgPSAiUm91dGUiKQogICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoUm93TnVtX09HLAogICAgICAgICBSb3V0ZSwKICAgICAgICAgUm91dGVHcm91cCwKICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgIHN0YXJ0c193aXRoKCJFdmVudCIpCiAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlR3JvdXAgPT0gaSkgJT4lICAjIG5lZWRlZCB0byBkbyB0aGlzIGVhY2ggUm91dGVHcm91cCAoMS01KSBiZWNhdXNlIHRoZSBjb21wbGV0ZSBmaWxlIHdhcyB0b28gbGFyZ2UgdG8gZG8gYXQgb25jZQogIGxlZnRfam9pbihSb3V0ZU1pbk1heCwKICAgICAgICAgICAgYnkgPSBjKCJSb3V0ZSIgPSAiUm91dGUiLAogICAgICAgICAgICAgICAgICAgIkV2ZW50X1RpbWVfRGF0ZSIgPSAiRXZlbnRfVGltZV9EYXRlIgogICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFNhbXBUaW1lID0gYXNfZGF0ZXRpbWUocnVuaWYobnJvdyguKSwgIzIwMDAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4gPSBNaW5UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heCA9IE1heFRpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiQW1lcmljYS9OZXdfWW9yayIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApIAoKIyBzdHIoU2FtcCkKIyBWaWV3KGhlYWQoU2FtcCwgNTAwKSkKIyAKIyBWaWV3KAojIGdyb3VwX2J5KFNhbXAsCiMgICAgICAgICAgUm93TnVtX09HCiMgICAgICAgICApICU+JQojICAgc3VtbWFyaXNlKENudF9OdW0gPSBuKCksCiMgICAgICAgICAgICAgQ250X1BjdCA9IDEwMCAqIENudF9OdW0gLyBucm93KFNhbXApCiMgICAgICAgICAgICApICU+JQojICAgYXJyYW5nZShkZXNjKENudF9OdW0pKQojICkKCgojIEZvciBlYWNoIFJvdXRlIGFuZCBTdG9wSUQgY29tYmluYXRpb24sIGdldCBhbGwgdGhlIEV2ZW50X1RpbWUgdmFsdWVzIHRoYXQgYXJlIGFmdGVyIHRoZSBTYW1wVGltZSB2YWx1ZS4KIyBlc3RpbWF0aW5nIGFwcHJveCAyaHJzIG9mIHJ1bnRpbWUgZm9yIGFsbCAyLjhtIHJlY29yZHMKVGVzdGluZ19BIDwtIHNxbGRmKCIgICBTZWxlY3QgICAgICAgICAgICAgICB0MS4qCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQyLkV2ZW50X1RpbWUgICAgICAgICAgICAgYXMgTmV4dEJ1cwogICAgICAgICAgICAgICAgICAgICAgICBGcm9tICAgICAgICAgICAgICAgICBTYW1wICAgICAgICAgICAgICAgICAgICAgIGFzIHQxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5uZXIgSm9pbiAgICAgIFNhbXAgICAgICAgICAgICAgICAgICAgICAgYXMgdDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPbiAgICAgICAgICAgICAgdDEuUm91dGUgPSB0Mi5Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFuZCAgICAgICAgICAgICB0MS5TdG9wSURfQ2xlYW4gPSB0Mi5TdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbmQgICAgICAgICAgICAgdDIuRXZlbnRfVGltZSA+IHQxLlNhbXBUaW1lCiAgICAgICAgICAgICAgICAgICAgICAgIE9yZGVyIEJ5ICAgICAgICAgICAgIHQxLlJvdXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQxLlN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0MS5FdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQyLkV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgIgogICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShOQiA9IGFzX2RhdGV0aW1lKE5leHRCdXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiQW1lcmljYS9OZXdfWW9yayIKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgojIHN0cihUZXN0aW5nX0EpCiMgVmlldyhoZWFkKFRlc3RpbmdfQSwgNTAwKSkKIyBWaWV3KGhlYWQoU2FtcCwgNTAwKSkKCgojIEZpbHRlciB0aGUgZGF0YWZyYW1lIHRvIG9ubHkgaW5jbHVkZSB0aGUgYnVzIGFycml2YWwgYXQgU3RvcElEIHRoYXQgaXMgdGhlIG5leHQgdG8gY29tZSBhZnRlciB0aGUgU2FtcFRpbWUuCiMgZXN0aW1hdGluZyBhcHByb3ggMjBtaW4gb2YgcnVudGltZSBmb3IgYWxsIDIuOG0gcmVjb3JkcwpUZXN0aW5nIDwtIHNlbGVjdChUZXN0aW5nX0EsCiAgICAgICAgICAgICAgICAgIC1OZXh0QnVzCiAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZ3JvdXBfYnkoUm93TnVtX09HKSAlPiUgCiAgZmlsdGVyKE5CID09IG1pbihOQikKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfTWluID0gYXMubnVtZXJpYyhOQiAtIFNhbXBUaW1lKSwKICAgICAgICAgV2FpdFRpbWVfU2VjID0gV2FpdFRpbWVfTWluICogNjAKICAgICAgICApICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKCmFzc2lnbihwYXN0ZTAoIlRlc3RpbmciLCBpKSwKICAgICAgIFRlc3RpbmcKICAgICAgKQoKcm0oU2FtcCxUZXN0aW5nX0EsIFRlc3RpbmcpCnN0cihnZXQocGFzdGUwKCJUZXN0aW5nIiwgaSkpKQpWaWV3KGdldChwYXN0ZTAoIlRlc3RpbmciLCBpKSkpCn0KCgojIEJpbmQgYWxsIHRoZSBpbmRpdmlkdWFsIGRhdGFmcmFtZXMgdG9nZXRoZXIuCldhaXREYXRhX1JvdXRlUHVsbCA8LSBiaW5kX3Jvd3MoVGVzdGluZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZzMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZzUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfU2VjMiA9IE5CIC0gU2FtcFRpbWUsCiAgICAgICAgIFdhaXRUaW1lX01pbjIgPSBXYWl0VGltZV9TZWMyIC8gNjAKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApCgpybShCdXNHcm91cHMsIGksIFRlc3RpbmczLCBUZXN0aW5nNCwgVGVzdGluZzUsIFRlc3Rpbmc2LCBUZXN0aW5nNykKc3RyKFdhaXREYXRhX1JvdXRlUHVsbCkKVmlldyhoZWFkKFdhaXREYXRhX1JvdXRlUHVsbCwgNTAwKSkKVmlldyh0YWlsKFdhaXREYXRhX1JvdXRlUHVsbCwgNTAwKSkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQ29tcGFyZSBXYWl0RGF0YSBwdWxsZWQgYnkgZGF5IGFuZCBwdWxsZWQgYnkgcm91dGUuCmBgYHtyfQoKZGltKFdhaXREYXRhX1JvdXRlUHVsbCkKZGltKFdhaXREYXRhX0RheVB1bGwpCm5yb3coV2FpdERhdGFfUm91dGVQdWxsKSAtIG5yb3coV2FpdERhdGFfRGF5UHVsbCkKCldhaXREYXRhX0RpZmYgPC0gYW50aV9qb2luKFdhaXREYXRhX1JvdXRlUHVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdERhdGFfRGF5UHVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJSb3dOdW1fT0ciID0gIlJvd051bV9PRyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdCgtV2FpdFRpbWVfTWluLAogICAgICAgICAtV2FpdFRpbWVfU2VjCiAgICAgICAgKQoKc3RyKFdhaXREYXRhX0RpZmYpClZpZXcoaGVhZChXYWl0RGF0YV9EaWZmLCA1MDApKQoKVmlldyhmaWx0ZXIoV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICBSb3V0ZSA9PSAiWjgiICYKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMjAwNTQ2NQogICAgICAgICAgICAjIFJvd051bV9PRyA9IDI5MDI3NjAKICAgICAgICAgICAgIyBFdmVudF9UaW1lID0gMjAxNi0xMC0wNyAxOTo1MTo0NwogICAgICAgICAgICkKICAgICkKClZpZXcoZ3JvdXBfYnkoV2FpdERhdGFfRGlmZiwKICAgICAgICAgICAgICBSb3V0ZQogICAgICAgICAgICAgKSAlPiUgCiAgICAgICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKICAgICAgICAgICAgICAgICBDbnRfUGN0ID0gQ250X051bSAvIG5yb3coV2FpdERhdGFfRGlmZikKICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKENudF9OdW0pCiAgICAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoV2FpdERhdGFfRGlmZiwKICAgICAgICAgICAgUm91dGUgPT0gIlMxIgogICAgICAgICAgICkKICAgICkKClZpZXcoZmlsdGVyKFdhaXREYXRhX1JvdXRlUHVsbCwKICAgICAgICAgICAgUm91dGUgPT0gIlMxIiAmCiAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDMxMzIKICAgICAgICAgICAgIyBSb3dOdW1fT0cgPSAxMTUxNzcwCiAgICAgICAgICAgICMgRXZlbnRfVGltZSA9IDIwMTYtMTAtMDcgMDk6MDc6MTIKICAgICAgICAgICApCiAgICApCgojIENhbid0IHRlbGwgd2h5IHRoZSBwdWxsIGJ5IGRheSBoYXMgbGVzcyByZWNvcmRzIHRoYW4gdGhlIHB1bGwgYnkgcm91dGUKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQ29tcGFyZSBXYWl0RGF0YSAocHVsbGVkIGJ5IHJvdXRlKSBhbmQgb3JpZ2luYWwgZGF0YSAoTmV3VHJhdlRpbWUpLgpgYGB7cn0KCiMgcm0oV2FpdERhdGFfRGlmZikKCgpkaW0oTmV3VHJhdlRpbWUpICAjIDIsODA5LDUyOSByb3dzCmRpbShXYWl0RGF0YV9Sb3V0ZVB1bGwpICAjIDIsNzgwLDg0OCByb3dzCm5yb3coTmV3VHJhdlRpbWUpIC0gbnJvdyhXYWl0RGF0YV9Sb3V0ZVB1bGwpICAjIGlzIDI4LDY4MSByb3dzCgpzdHIoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQpzdHIoV2FpdERhdGFfUm91dGVQdWxsKQoKQ29tcGFyZV9OVFRfV0QgPC0gbGVmdF9qb2luKE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFdhaXREYXRhX1JvdXRlUHVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dOdW1fT0csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZUdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3RvcElEX0NsZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRXZlbnRfVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNaW5UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1heFRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcFRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfU2VjMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJSb3dOdW1fT0ciID0gIlJvd051bV9PRyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKQoKc3RyKENvbXBhcmVfTlRUX1dEKSAgIyAyLDgxMCwxMDkgcm93cyBvdmVyYWxsICAtLSAgMjksMjYxIHJvd3Mgd2l0aCBubyBtYXRjaApWaWV3KGhlYWQoQ29tcGFyZV9OVFRfV0QsIDUwMCkpClZpZXcoZmlsdGVyKENvbXBhcmVfTlRUX1dELAogICAgICAgICAgICBpcy5uYShNaW5UaW1lKQogICAgICAgICAgICkKICAgICkKCgoKVmlldyhhbnRpX2pvaW4oU2FtcCwKICAgICAgICAgICAgICAgZGlzdGluY3Qoc2VsZWN0KFdhaXREYXRhX1JvdXRlUHVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvd051bV9PRwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgYnkgPSBjKCJSb3dOdW1fT0ciID0gIlJvd051bV9PRyIpCiAgICAgICAgICAgICAgKQogICAgKQoKIyBUaGUgU2FtcFRpbWUgaXMgQUZURVIgdGhlIGxhc3QgYnVzIHBhc3NlZCB0aGF0IFN0b3BJRF9DbGVhbgpWaWV3KGZpbHRlcihTYW1wLAogICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAzNzc0CiAgICAgICAgICAgICMgUm93TnVtX09HID0gMTE0NjcyMwogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE1OjMyOjE4CiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKQ2xlYW4gdXAgdGhlIGRhdGEgYSBiaXQuCmBgYHtyfQoKcm0oQnVzR3JvdXBzLCBSb3V0ZU1pbk1heCwgU2FtcCwgVGVzdGluZzEsIFRlc3RpbmcyLCBUZXN0aW5nMywgVGVzdGluZzQsIFRlc3Rpbmc1LCBUZXN0aW5nXzMsIFRlc3RpbmdfNCwgVGVzdGluZ181LCBUZXN0aW5nXzYsIFRlc3RpbmdfNywgV2FpdERhdGFfRGF5UHVsbCwgV2FpdERhdGFfRGlmZikKCgpzdHIoQ29tcGFyZV9OVFRfV0QpClZpZXcoaGVhZChDb21wYXJlX05UVF9XRCwgNTAwKSkKVmlldyhoZWFkKG11dGF0ZShDb21wYXJlX05UVF9XRCwKICAgICAgICAgICAgICAgICBXVF9NaW4gPSBhcy5udW1lcmljKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICkKICAgICkKCldhaXRUaW1lX0FzTnVtIDwtIENvbXBhcmVfTlRUX1dEICU+JSAKICBtdXRhdGUoUm91dGVTdG9wX0lEID0gZmFjdG9yKHBhc3RlKFJvdXRlLCBTdG9wSURfQ2xlYW4sIHNlcCA9ICJfXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCldhaXRUaW1lX0FzTnVtJFdhaXRUaW1lX1NlYzIgPC0gYXMubnVtZXJpYyhXYWl0VGltZV9Bc051bSRXYWl0VGltZV9TZWMyKQpXYWl0VGltZV9Bc051bSRXYWl0VGltZV9NaW4yIDwtIGFzLm51bWVyaWMoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMikKCnJtKENvbXBhcmVfTlRUX1dEKQpzdHIoV2FpdFRpbWVfQXNOdW0pCgpgYGAKCgpHZW5lcmFsIGV4cGxvcmF0aW9uIG9mIHdhaXQgdGltZXMuCmBgYHtyfQoKc3VtbWFyeShXYWl0VGltZV9Bc051bSRXYWl0VGltZV9NaW4yKQoKV1RfUXVhbnRpbGVzIDwtIGFzLmRhdGEuZnJhbWUocXVhbnRpbGUoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpjb2xuYW1lcyhXVF9RdWFudGlsZXMpIDwtICJWYWx1ZV9NaW4iCgpXVF9RdWFudGlsZXMkVmFsdWVfU2VjID0gZm9ybWF0KHJvdW5kKFdUX1F1YW50aWxlcyRWYWx1ZV9NaW4gKiA2MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWdpdHMgPSAyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKV1RfUXVhbnRpbGVzJFZhbHVlX0hyID0gZm9ybWF0KHJvdW5kKFdUX1F1YW50aWxlcyRWYWx1ZV9NaW4gLyA2MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCldUX1F1YW50aWxlcyRWYWx1ZV9NaW4gPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWdpdHMgPSAyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCldUX1F1YW50aWxlcyRRdWFudGlsZSA8LSBzZXEoMCwgMSwgMC4wMSkKCldUX1F1YW50aWxlcyA8LSBzZWxlY3QoV1RfUXVhbnRpbGVzLAogICAgICAgICAgICAgICAgICAgICAgIFF1YW50aWxlLAogICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX1NlYywKICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfSHIKICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihXVF9RdWFudGlsZXMpClZpZXcoV1RfUXVhbnRpbGVzKQoKClZpZXcoYXJyYW5nZShXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgIGRlc2MoV2FpdFRpbWVfTWluMikKICAgICAgICAgICAgKSAlPiUgCiAgICAgICBoZWFkKC4sIDUwMDApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgYmV0d2VlbihXYWl0VGltZV9NaW4yLCA2MCwgMjAwKQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgKSAKICAgICAjICU+JSAKICAgICAjICAgaGVhZCguLCA1MDAwKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlcxMyIgJiAgIyBvbmx5IDIgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzcyOAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTAzIDA4OjQyOjQ2CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlM0MSIgJiAgIyBvbmx5IDQgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMTA5NQogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA1IDE1OjQxOjQ3CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIkQ4IiAmICAjIHJvdXRlIGhhcyBWRVJZIGxpbWl0ZWQgc2VydmljZSBhZnRlciBtaWRuaWdodAogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAxNjY5CiAgICAgICAgICAgICMgRXZlbnRfVGltZSA9IDIwMTYtMTAtMDYgMjA6MzE6MTYKICAgICAgICAgICApCiAgICApCgpgYGAKCgpMb29rcyBsaWtlIHRoZXJlIG1pZ2h0IGJlIGFuIGlzc3VlIGluIHdhaXQgdGltZXMgd2hlbiB2ZXJ5IGZldyBSb3V0ZS1TdG9wIGNvbWJpbmF0aW9ucyBhcmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQuICBMZXQncyBleHBsb3JlIHRoZXNlLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzIDwtIGdyb3VwX2J5KFdhaXRUaW1lX0FzTnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZVN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wX0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wX0NudFBjdCA9IFJvdXRlU3RvcF9DbnROdW0gLyBucm93KFdhaXRUaW1lX0FzTnVtKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKClZpZXcoUm91dGVTdG9wX0NudHMpCgoKUm91dGVTdG9wX0NudE9mQ250IDwtIGdyb3VwX2J5KFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wQ250X0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wQ250X0NudFBjdCA9IFJvdXRlU3RvcENudF9DbnROdW0gLyBucm93KFJvdXRlU3RvcF9DbnRzKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bSA9IGN1bXN1bShSb3V0ZVN0b3BDbnRfQ250UGN0KSwKICAgICAgICAgeCA9IDEgLSBSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKICAKIFZpZXcoUm91dGVTdG9wX0NudE9mQ250KQoKYGBgCgoKSGlzdG9ncmFtIG9mIHRoZSBjb3VudHMgb2YgUm91dGUtU3RvcElEIGNvbWJpbmF0aW9ucy4KYGBge3J9CgpSb3V0ZVN0b3BfQ250c19CYXIgPC0gZ2dwbG90KFJvdXRlU3RvcF9DbnRPZkNudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFJvdXRlU3RvcF9DbnROdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBSb3V0ZVN0b3BDbnRfQ250TnVtCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgIyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDUwMCkKICAgICAgICAgICAgICAgICAgIyB5bGltID0gYygwLCAwLjAyKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIFJvdXRlcyBQYXNzaW5nIGEgU3BlY2lmaWMgU3RvcCIsCiAgICAgICB4ID0gIk9jY3VycmVuY2VzIG9mIFJvdXRlLVN0b3BJRCBDb21iaWFudGlvbnMiLAogICAgICAgeSA9ICJDb3VudHMiCiAgICAgICkKClJvdXRlU3RvcF9DbnRzX0JhcgoKYGBgCgoKQ3JlYXRlIGEgbmV3IGRhdGFzZXQgbGltaXRpbmcgZXh0cmVtZWx5IHNtYWxsIGNvdW50cyBvZiBSb3V0ZS1TdG9wSUQgY29tYmluYXRpb25zLgpgYGB7cn0KCldhaXRUaW1lX1J0ZUNudHMgPC0gbGVmdF9qb2luKFdhaXRUaW1lX0FzTnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZVN0b3BfQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJSb3V0ZVN0b3BfSUQiID0gIlJvdXRlU3RvcF9JRCIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2VsZWN0KC1Sb3V0ZVN0b3BfQ250UGN0KQoKZGltKFdhaXRUaW1lX0FzTnVtKQpkaW0oV2FpdFRpbWVfUnRlQ250cykKCnJtKFdhaXRUaW1lX0FzTnVtKQpzdHIoV2FpdFRpbWVfUnRlQ250cykKCgojIFRvdGFsIHJvd3MKbnJvdyhXYWl0VGltZV9SdGVDbnRzKQoKIyBSb3dzIG9mIHJhcmUgUm91dGVTdG9wcwpucm93KGZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICBSb3V0ZVN0b3BfQ250TnVtIDw9IDYwCiAgICAgICAgICAgKQogICAgKSAvIG5yb3coV2FpdFRpbWVfUnRlQ250cykKCiMgUm93cyBvZiBleHRyZW1lbHkgbG9uZyB3YWl0IHRpbWVzCm5yb3coZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPiAxODAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhXYWl0VGltZV9SdGVDbnRzKQoKCnNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgV2FpdFRpbWVfTWluMgogICAgICApICU+JSAKICBzdW1tYXJ5KCkKCmZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgUm91dGVTdG9wX0NudE51bSA+IDYwICAjIDEyIHBhc3NlcyBwZXIgZGF5IGluIGEgNS1kYXkgZGF0YXNldAogICAgICApICU+JSAKICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogIHN1bW1hcnkoKQoKZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBXYWl0VGltZV9NaW4yIDwgMTgwICAjIHByb2JhYmx5IG1lYW5zIHRoYXQgc29tZXRoaW5nIHdlbnQgd3JvbmcKICAgICAgKSAlPiUgCiAgc2VsZWN0KFdhaXRUaW1lX01pbjIpICU+JSAKICBzdW1tYXJ5KCkKCgoKYSA8LSBhcy5kYXRhLmZyYW1lKHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpiIDwtIGFzLmRhdGEuZnJhbWUoZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bSA+IDYwCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFdhaXRUaW1lX01pbjIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUocHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgKQoKYyA8LSBhcy5kYXRhLmZyYW1lKGZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpXVF9GaWx0ZXJfUXVhbnRpbGVzIDwtIGJpbmRfY29scyhhLCBiLCBjKSAlPiUgCiAgbXV0YXRlKFF1YW50aWxlID0gc2VxKDAsIDEsIDAuMDEpCiAgICAgICAgKQoKY29sbmFtZXMoV1RfRmlsdGVyX1F1YW50aWxlcykgPC0gYygiQWxsIiwgIlJ0ZVN0cEFidjYwIiwgIldUQmx3MTgwIiwgIlF1YW50aWxlIikKcm0oYSwgYiwgYykKVmlldyhXVF9GaWx0ZXJfUXVhbnRpbGVzKQoKYGBgCgoKSGlzdG9ncmFtIG9mIGFsbCB3YWl0IHRpbWVzLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19IaXN0RGVuIDwtIGdncGxvdChmaWx0ZXIoc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShXYWl0VGltZV9NaW4yKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDMwMCwgMzApCiAgICAgICAgICAgICAgICAgICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDMwMCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDAuMDM1KQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIFdhaXQgVGltZSIsCiAgICAgICB4ID0gIldhaXQgVGltZSAobWluKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IaXN0RGVuCgpgYGAKCgpCb3ggcGxvdHMgZm9yIFdhaXRUaW1lIChhbGwgYnVzc2VzLCBieSBaaXAgQ29kZSkuCmBgYHtyfQoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpCdXNSb3V0ZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgIFJvdXRlLAogICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwCiAgICAgICAgICAgICAgICAgICkgJT4lIAogIGZpbHRlcihSb3V0ZSA9PSAiWDIiKQoKQ291bnRWYWx1ZXNfQWxsQnVzX1ppcCA8LSBkZHBseShCdXNSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFdhaXRUaW1lX01pbjIsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19aaXBfQm94IDwtIGdncGxvdChCdXNSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmYWN0b3IoU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihTdG9wX1ppcCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfWmlwLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQ1KQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiV2FpdGluZyBUaW1lIGF0IGEgR2l2ZW4gU3RvcCAoZm9yIHRoZSBYMikiLAogICAgICAgeCA9ICJaaXAgQ29kZSBvZiBEZXN0aW5hdGlvbiIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX1ppcF9Cb3gKCmBgYAoKClRlc3QgaW52ZXN0aWdhdGlvbiBvZiBqdXN0IHRoZSBYMiBSb3V0ZS4gVmlvbGluIHBsb3RzIGZvciB0aW1lIGJldHdlZW4gYnVzIGFycml2YWxzIChieSBaaXAgQ29kZSkuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX1ppcF9WaW9saW4gPC0gZ2dwbG90KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUgPSAiY291bnQiLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IE5BLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gVFJVRQogICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfWmlwLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDMuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIChmb3IgdGhlIFgyKSIsCiAgICAgICB4ID0gIlppcCBDb2RlIG9mIERlc3RpbmF0aW9uIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3RfegoKYGBgCgoKQm94IHBsb3RzIGZvciBXYWl0VGltZSAoWmlwIENvZGUsIGJ5IEhvdXJHcm91cFppcCkuCmBgYHtyfQoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpaaXAgPC0gc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICBTdG9wX1ppcCwKICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICkgJT4lIAogIGZpbHRlcihTdG9wX1ppcCA9PSAyMDAwMikKCkNvdW50VmFsdWVzX0FsbEJ1c19IRyA8LSBkZHBseShaaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IR19Cb3ggPC0gZ2dwbG90KFppcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA0NSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciBaaXAgMjAwMDIpIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKQogICMgZmFjZXRfd3JhcCh+U3RvcF9aaXAKICAjICAgICAgICAgICAgIyBucm93ID0gNQogICMgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IR19Cb3gKCmBgYAoKClZpb2xpbiBwbG90cyBmb3IgV2FpdFRpbWUgKFppcCBDb2RlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19IR19WbG4gPC0gZ2dwbG90KFppcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRWYWx1ZXNfQWxsQnVzX0hHLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgOTApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIChmb3IgWmlwIDIwMDAyKSIsCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkKICAjIGZhY2V0X3dyYXAoflN0b3BfWmlwCiAgIyAgICAgICAgICAgICMgbnJvdyA9IDUKICAjICAgICAgICAgICApCgpXYWl0VGltZV9BbGxCdXNfSEdfVmxuCgpgYGAKCgpCb3ggcGxvdHMgZm9yIFdhaXRUaW1lIChSb3V0ZSwgYnkgSG91ckdyb3VwWmlwKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzClJ0ZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpDb3VudFZhbHVlc19BbGxCdXNfUnRlSEcgPC0gZ3JvdXBfYnkoUnRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZDID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogIAogICMgZGRwbHkoUnRlLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFdhaXRUaW1lX01pbjIsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3ggPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfUnRlSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgoQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHJFZDKSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgc3VidGl0bGUgPSAoIlJvdXRlIFgyIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgCiMgKwojICAgZmFjZXRfd3JhcCh+U3RvcF9aaXAKIyAgICAgICAgICAgICAgIyBucm93ID0gNQojICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3gKCmBgYAoKClZpb2xpbiBwbG90cyBmb3IgV2FpdFRpbWUgKFppcCBDb2RlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4gPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgKwogIGZhY2V0X3dyYXAoflN0b3BfWmlwCiAgICAgICAgICAgICAjIG5yb3cgPSA1CiAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4KCmBgYAoKClgyIFBlcmNlbnRpbGVzIExpbmUgR3JhcGggVGVzdC4KYGBge3J9CgpYMl9QY3QgPC0gc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyLAogICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikgJT4lIAogIGdyb3VwX2J5KEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShQY3Q1MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC41LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q2MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q3MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC43LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q4MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q5MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC45LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKQoKc3RyKFgyX1BjdCkKVmlldyhYMl9QY3QpCgoKWDJfTG9uZyA8LSBnYXRoZXIoWDJfUGN0LAogICAgICAgICAgICAgICAgICBrZXkgPSBQZXJjZW50aWxlLAogICAgICAgICAgICAgICAgICB2YWx1ZSA9IFBjdGlsZSwKICAgICAgICAgICAgICAgICAgUGN0NTAsCiAgICAgICAgICAgICAgICAgIFBjdDYwLAogICAgICAgICAgICAgICAgICBQY3Q3MCwKICAgICAgICAgICAgICAgICAgUGN0ODAsCiAgICAgICAgICAgICAgICAgIFBjdDkwCiAgICAgICAgICAgICAgICApCgpzdHIoWDJfTG9uZykKVmlldyhYMl9Mb25nKQoKClgyX1dhaXRCeUhyX0xpbmUgPC0gZ2dwbG90KFgyX0xvbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gRXZlbnRfVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQY3RpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3IoUGVyY2VudGlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fbGluZSgpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMjMpCiAgICAgICAgICAgICAgICAgICMgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDIpCiAgICAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgVGhyb3VnaG91dCB0aGUgRGF5IiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgb2YgdGhlIERheSIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKSArCiAgZmFjZXRfd3JhcCh+U3RvcF9aaXApCgpYMl9XYWl0QnlIcl9MaW5lCgpgYGAKCgoKCgpHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkKYGBge3J9CgojIHN0cihXYWl0VGltZV9SdGVDbnRzKQoKU2hpbnlfV2FpdERhdGFfQmFzZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpTaGlueV9XYWl0RGF0YV9CYXNlJFJvdXRlIDwtIGZhY3RvcihTaGlueV9XYWl0RGF0YV9CYXNlJFJvdXRlKQoKc3RyKFNoaW55X1dhaXREYXRhX0Jhc2UpCgp3cml0ZS50YWJsZShTaGlueV9XYWl0RGF0YV9CYXNlLAogICAgICAgICAgICAiU2hpbnlfV2FpdERhdGFfQmFzZS50eHQiLAogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLAogICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRQogICAgICAgICAgICkKIyBybShTaGlueV9XYWl0RGF0YV9CYXNlKQoKYGBgCgoKClRlc3RpbmcgbWFwcGluZyBmdW5jdGlvbmFsdGl5CmBgYHtyfQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRrYWhsZS9nZ21hcCIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJoYWRsZXkvZ2dwbG90MiIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dtYXAiLCB0eXBlID0gInNvdXJjZSIpCgojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YignaGFkbGV5L2dncGxvdDInKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImhhZGxleS9nZ3Bsb3QyQHYyLjIuMCIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCd0aG9tYXNwODUvZ2dmb3JjZScpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCd0aG9tYXNwODUvZ2dyYXBoJykKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ3Nsb3drb3cvZ2dyZXBlbCcpCgoKCgoKCgp0cmFjdCA8LSAKICByZWFkT0dSKGRzbiA9ICIvVXNlcnMvbWR0dXJzZS9EZXNrdG9wL0FuYWx5dGljcy9EQ01ldHJvQnVzL2NiXzIwMTVfMTFfdHJhY3RfNTAwayIsCiAgICAgICAgICBsYXllciA9ICJjYl8yMDE1XzExX3RyYWN0XzUwMGsiCiAgICAgICAgICkKICAKY2xhc3ModHJhY3QpCgoKIyBjb252ZXJ0IHRoZSBHRU9JRCB0byBhIGNoYXJhY3Rlcgp0cmFjdEBkYXRhJEdFT0lEIDwtIGFzLmNoYXJhY3Rlcih0cmFjdEBkYXRhJEdFT0lEKQoKc3RyKHRyYWN0QGRhdGEpCgoKCmdndHJhY3QgPC0gdGlkeSh0cmFjdCkjLCByZWdpb24gPSAiR0VPSUQiKQpzdHIoZ2d0cmFjdCkKc3VtbWFyeShnZ3RyYWN0KQoKCgptYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGMobG9uID0gLTc3LjAzNjc2LCBsYXQgPSAzOC44OTc4NCksCiAgICAgICAgICAgICAgIHNvdXJjZSA9ICJnb29nbGUiLAogICAgICAgICAgICAgICAjIG1hcHR5cGUgPSAicm9hZG1hcCIKICAgICAgICAgICAgICAgem9vbSA9IDEyCiAgICAgICAgICAgICAgKQoKZ2dtYXAobWFwKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIAogICAgICAgICAgICAgICBkYXRhID0gZ2d0cmFjdCwKICAgICAgICAgICAgICAgY29sb3VyID0gJ3doaXRlJywgCiAgICAgICAgICAgICAgIGZpbGwgPSAnYmxhY2snLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAuNCwgCiAgICAgICAgICAgICAgIHNpemUgPSAuMwogICAgICAgICAgICAgICkKICAKCgoKCgoKWmlwV2FpdFRlc3QgPC0gZmlsdGVyKFNoaW55X1dhaXREYXRhX0Jhc2UsCiAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yIDw9IDE4MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTdG9wX1ppcCkKICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZ3JvdXBfYnkoU3RvcF9aaXAsCiAgICAgICAgICAgRXZlbnRfVGltZV9IcgogICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKFBjdDgwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjgsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICApICU+JSAKICBhcnJhbmdlKEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICBTdG9wX1ppcAogICAgICAgICApCgoKVmlldyhoZWFkKFppcFdhaXRUZXN0LCA1MDApKQoKCgoKYGBgCgoKSW52ZXN0aWdhdGluZyBUcmF2ZWxUaW1lX1NlYy4KYGBge3J9CgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgc21hbGwgKDEgc2VjKSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgbGFyZ2UuClZpZXcoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAjIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgLShURF9NaV9xMjpURF9NaV9TU0hHX0NudF9GKSwKICAgICAgICAgICAgLShUVF9Icl9xMjpUVF9Icl9TU0hHX0NudF9GKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMjIxNzM1MyAmIFJvd051bV9PRyA8PSAyMjE3MzczKSB8ICMgMjIxNzM2MwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDkwMzIxICYgUm93TnVtX09HIDw9IDMwOTAzNDEpIHwgIyAzMDkwMzMxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwNzY0ICYgUm93TnVtX09HIDw9IDgwNzg0KSB8ICMgODA3NzQKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzM4NDAgJiBSb3dOdW1fT0cgPD0gMzM4NjApICMgMzM4NTAKICAgICAgICAgICApCiAgICApCgoKCgoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgbGFyZ2UgYW5kIFNwZWVkQXZnX01waF9OZXdIdnJzIGlzIHNtYWxsLgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMjUwMjkwICYgUm93TnVtX09HIDw9IDIyNTAzMTApIHwgIyAyMjUwMzAwCiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4Njc3MTcgJiBSb3dOdW1fT0cgPD0gODY3NzM3KSB8ICMgODY3NzI3CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4NjQzNzkgJiBSb3dOdW1fT0cgPD0gODY0Mzk5KSB8ICMgODY0Mzg5CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4MDgzOTUgJiBSb3dOdW1fT0cgPD0gODA4NDE1KSAjIDgwODQwNQogICAgICAgICAgICkKICAgICkKYGBgCgoKCgpgYGB7cn0KCiAgICAgICAgIAogICAgICAgICAKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgc21hbGwgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIGxhcmdlKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDEwNDIyMjggJiBSb3dOdW1fT0cgPD0gMTA0MjI0OCkgfCAjIDEwNDIyMzgKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNTM4MTYgJiBSb3dOdW1fT0cgPD0gNTM4MzYpIHwgIyA1MzgyNgogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzNjA1NzEgJiBSb3dOdW1fT0cgPD0gMzYwNTkxKSB8ICMgMzYwNTgxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDUwMjI3MSAmIFJvd051bV9PRyA8PSA1MDIyOTEpICMgNTAyMjgxIChjYW4ndCBleHBsaWFuIHRoZSB3ZWlyZCBUcmF2ZWxUaW1lX1NlYyBjYWxjdWxhdGlvbiBoZXJlIC0gaXQncyBub3QgZXZlbiBhbiBpbnRlZ2VyISkKICAgICAgICAgICApCiAgICApCgojIHN0aWxsIHRyeWluZyB0byBleHBsYWluIDUwMjI4MS4uLm9uIHRoZSBkYXkgb2YgdGhpcyB3ZWlyZG5lc3MsIHRoZSBidXMgd2FzIG9ubHkgaW4gY2lyY3VsYXRpb24gZm9yIDQtNSBzdG9wcyAofjIwIG1pbnV0ZXMpIG9uIHRoYXQgZGF5IChPY3QgNikKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICBCdXNfSUQgPT0gMjcxMQogICAgICAgICAgICkKICAgICkKCgojIGV4cGxvcmluZyBsYXJnZSB2YWx1ZXMgZm9yIFRyYXZlbFRpbWVfU2VjClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPT0gMzAwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgbGFyZ2UgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIHNtYWxsLCBzbyBTcGVlZEF2Z19NcGggdmFsdWVzIGFyZSB2ZXJ5IHNtYWxsKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDI2Mjc0NTkgJiBSb3dOdW1fT0cgPD0gMjYyNzQ3OSkgfCAjIDI2Mjc0NjkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjE5MzM0NCAmIFJvd051bV9PRyA8PSAyMTkzMzY0KSB8ICMgMjE5MzM1NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjQ0MTIzICYgUm93TnVtX09HIDw9IDE2NDQxNDMpIHwgIyAxNjQ0MTMzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2OTYwMCAmIFJvd051bV9PRyA8PSA4Njk2MjApICMgODY5NjEwCiAgICAgICAgICAgKQogICAgKQoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIKClZpZXcoU3BlZWRfUGN0aWxlcyk6IDkwJSBvZiBTcGVlZEF2Z19NcGgyIGFyZSBiZXR3ZWVuIH4zbXBoIGFuZCB+NjZtcGguCmBgYHtyfQoKU3BlZWRfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhTcGVlZF9OdGlsZSlbMV0gPC0gIlNwZWVkQXZnX01waDIiCnN0cihTcGVlZF9OdGlsZSkKClNwZWVkX050aWxlX1Jvd3MgPC0gbnJvdyhTcGVlZF9OdGlsZSkKClZpZXcodGFpbChTcGVlZF9OdGlsZSwgNTAwKSkKCgpTcGVlZF9QY3RpbGVzIDwtIGdyb3VwX2J5KFNwZWVkX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblNwZWVkQXRQY3RpbGUgPSBtaW4oU3BlZWRBdmdfTXBoMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBTcGVlZF9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKClZpZXcoU3BlZWRfUGN0aWxlcykKCmBgYAoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKRXhwbG9yaW5nIHRoZSByZW1vdmFsIG9mIG91dGxpZXIgVHJhdmVsVGltZV9TZWMgYW5kIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KCnN1bW1hcnkoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gMC4wMDAxODkzOTM5ICYgIyBsb3dlc3Qgbm9uLXplcm8gcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTAuMDUwMDAwICYgIyAybmQgcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIuCmBgYHtyfQoKU3BlZWRfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFNwZWVkQXZnX01waDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHN0YXRfYmluKGJpbndpZHRoID0gNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICkgKwogICMgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICMgICAgICAgICAgICAgICksCiAgIyAgICAgICAgICAgc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgbnVkZ2VfeSA9ICguLmNvdW50Li4gKiAwLjEpCiAgIyAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNzApLCB5bGltID0gYygwLCAwLjA0KQogICAgICAgICAgICAgICAgICkgKwogICMgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBUcmF2ZWwgU3BlZWQiLAogICAgICAgeCA9ICJBdmVyYWdlIFNwZWVkIChtcGgpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKU3BlZWRfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIgYWZ0ZXIgcmVtb3Zpbmcgb3V0bGllciBUcmF2ZWxUaW1lX1NlYyBhbmQgVHJhdmVsRGlzdGFuY2VfTWkuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKU3BlZWROb091dGxpZXJfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IDAuMDc3ODQxMDA1ICYgIyA1dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ldyA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTIuMTAwMDAwICMgNHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBTcGVlZEF2Z19NcGgyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgIHZqdXN0ID0gMS41LAogICAgICAgICAgIGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICAgICAgICAgICAgICksCiAgICAgICAgICApICsKICAjIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQoLi5jb3VudC4uLCBiaWcubWFyayA9ICIsIikKICAjICAgICAgICAgICAgICApLAogICMgICAgICAgICAgIHNpemUgPSAzLAogICMgICAgICAgICAgIG51ZGdlX3kgPSAoLi5jb3VudC4uICogMC4xKQogICMgICAgICAgICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDcwKSwgeWxpbSA9IGMoMCwgMC4wNCkKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFNwZWVkIiwKICAgICAgIHN1YnRpdGxlID0gIihyZW1vdmVkIGxvdyBvdXRsaWVycyBvZiBUcmF2ZWwgRGlzdGFuY2UgYW5kIFRyYXZlbCBUaW1lKSIsCiAgICAgICB4ID0gIkF2ZXJhZ2UgU3BlZWQgKG1waCkiLAogICAgICAgeSA9ICJEZW5zaXR5IgogICAgICApCgpTcGVlZE5vT3V0bGllcl9IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpOZXcgZGF0YXNldCAoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpIHdoZW4gcmVtb3Zpbmcgb3V0bGllciBsb3cgdmFsdWVzIG9mIFRyYXZlbERpc3RhbmNlX01pX05ldyBhbmQgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUgPC0gZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IC4wNzc4NDEwMDUgJiAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgMS4wODEyNTAwMDAwICYgIyA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiAxMi4xMDAwMDAgIyA0dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbFRpbWVfU2VjIDwgMjkzLjAwMDAwMCAjIDk4dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCkgLSBucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKc3RyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQpzdW1tYXJ5KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcHBlZEF2Z19NcGgyLgoKVmlldyhTcGVlZF9Ob091dF9QY3RpbGVzKTogIEFwcm94aW1hdGVseSA5MCUgb2YgU3BlZWRBdmdfTXBoMiB2YWx1ZXMgYXJlIGJldHdlZW4gfjRtcGggYW5kIH41Nm1waC4KYGBge3J9CgpTcGVlZF9Ob091dF9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoU3BlZWRfTm9PdXRfTnRpbGUpWzFdIDwtICJTcGVlZEF2Z19NcGgyIgpzdHIoU3BlZWRfTm9PdXRfTnRpbGUpCgpTcGVlZF9Ob091dF9OdGlsZV9Sb3dzIDwtIG5yb3coU3BlZWRfTm9PdXRfTnRpbGUpCgpWaWV3KHRhaWwoU3BlZWRfTm9PdXRfTnRpbGUsIDUwMCkpCgoKU3BlZWRfTm9PdXRfUGN0aWxlcyA8LSBncm91cF9ieShTcGVlZF9Ob091dF9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5TcGVlZEF0UGN0aWxlID0gbWluKFNwZWVkQXZnX01waDIpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gU3BlZWRfTm9PdXRfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KFNwZWVkX05vT3V0X1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwcGVkQXZnX01waDIuCgpFeGxvcmluZyBvZGQvaW1wb3NzaWJsZSB2YWx1ZXMuCmBgYHtyfQoKIyBFeHBsb3Jpbmcgd2hlbiBTcGVlZEF2Z19NcGgyIGlzIE5BICAtLSAgZG9lcyBub3Qgb2NjdXIgYXQgYWxsCm5yb3coZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICBpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICkKICAgICkKCgojIEV4cGxvcmluZyB3aGVuIFNwZWVkQXZnX01waDIgaXMgemVybyAgLS0gIGRvZXMgbm90IG9jY3VyIGF0IGFsbApucm93KGZpbHRlcihOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSwKICAgICAgICAgICAgU3BlZWRBdmdfTXBoMiA9PSAwCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgU3BlZWRBdmdfTXBoMiA8IDMuMjg0ODc3MApWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIFNwZWVkQXZnX01waDIgPiAwICYKICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyIDwgMy4yODQ4NzcwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFNwZWVkQXZnX01waDIpCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFNwZWVkQXZnX01waDIgPCAzLjI4NDg3NzAKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDQ4NTMzOCAmIFJvd051bV9PRyA8PSA0ODUzNTgpIHwgIyA0ODUzNDggIC0tICBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzQ2OTUyICYgUm93TnVtX09HIDw9IDM0Njk3MikgfCAjIDM0Njk2MiAgLS0gRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3MDQ5NCAmIFJvd051bV9PRyA8PSA3MDUxNCkgfCAjIDcwNTA0ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDIwNTE4NDYgJiBSb3dOdW1fT0cgPD0gMjA1MTg2NikgIyAyMDUxODU2ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKTGltaXQgdGhlIGRhdGFzZXQgYmFzZWQgb24gU3BlZWRBdmdfTXBoMi4KYGBge3J9CgpOb091dGxpZXJzU3BlZWQgPC0gZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oU3BlZWRBdmdfTXBoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQuMDY5MzAwLCAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1Ni4wNTY1MSAjOTV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKSAtIG5yb3coTm9PdXRsaWVyc1NwZWVkKQoKc3VtbWFyeShOb091dGxpZXJzU3BlZWQpCgpgYGAKCgpUcmF2ZWxUaW1lIG5vdyBsb29rcyBsaWtlIGl0IGhhcyBzb21lIG9kZCB2YWx1ZXMgb24gdGhlIGhpZ2ggZW5kLiAgU28gbGV0J3MgbG9vayBhdCB0aG9zZS4KClZpZXcoVHJhdlRpbWVfTm9PdXRfUGN0aWxlcyk6ICBWaXJ0dWFsbHkgYWxsIHRyaXBzIHNob3VsZCB0YWtlIGxlc3MgdGhhbiA1IG1pbnV0ZXMuIChUaGUgOTl0aCBwZXJjZW50aWxlIG9mIG9mIFRyYXZlbFRpbWUgaXMgYXBwcm94aW1hdGVseSA4IG1pbnV0ZXMuKQpgYGB7cn0KClRyYXZUaW1lX05vT3V0X050aWxlIDwtIGFzLmRhdGEuZnJhbWUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgIE1pblIgPSBtaW5fcmFuayhOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApCgpjb2xuYW1lcyhUcmF2VGltZV9Ob091dF9OdGlsZSlbMV0gPC0gIlRyYXZlbFRpbWVfSHIiCnN0cihUcmF2VGltZV9Ob091dF9OdGlsZSkKClRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MgPC0gbnJvdyhUcmF2VGltZV9Ob091dF9OdGlsZSkKClZpZXcodGFpbChUcmF2VGltZV9Ob091dF9OdGlsZSwgNTAwKSkKCgpUcmF2VGltZV9Ob091dF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lX05vT3V0X050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpLAogICAgICAgICBNaW5UcmF2VGltZVNlY0F0UGN0aWxlID0gTWluVHJhdlRpbWVIckF0UGN0aWxlICogKDYwICogNjApCiAgICAgICAgKQoKVmlldyhUcmF2VGltZV9Ob091dF9QY3RpbGVzKQoKYGBgCgoKSW52ZXN0aWdhdGluZyBvZGQgVHJhdmVsVGltZV9TZWMgdmFsdWVzLgoKVHJpcHMgbG9uZ2VyIHRoYW4gfjggbWludXRlcy4KYGBge3J9CgpWaWV3KGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gNDkxICMgbWluIGF0IHRoZSAxMDB0aCBwZXJjZW50aWxlCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgbGFyZ2VzdC4KVmlldyhmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIwNzE3NTkgJiBSb3dOdW1fT0cgPD0gMjA3MTc3OSkgfCAjIDIwNzE3NjkgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNocisgd2FpdCBiZWZvcmUgdGhlIG5ldyByb3V0ZSBzdGFydHMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTQ3MzY4NiAmIFJvd051bV9PRyA8PSAxNDczNzA2KSB8ICMgMTQ3MzY5NiAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEyMjI4MjIgJiBSb3dOdW1fT0cgPD0gMTIyMjg0MikgfCAjIDEyMjI4MzIgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNociB3YWl0IGJlZm9yZSB0aGUgbmV3IHJvdXRlIHN0YXJ0cwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ2MDg5ICYgUm93TnVtX09HIDw9IDMwNDYxMDkpICMgMzA0NjA5OSAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgb2YgVHJhdmVsVGltZV9TZWMgdmFsdWVzIHRoYXQgYXJlIHRoZSBzbWFsbGVzdCBvZiB0aGUgbGFyZ2UuClZpZXcoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ0Njg5ICYgUm93TnVtX09HIDw9IDMwNDQ3MDkpIHwgIyAzMDQ0Njk5ICAtLSAgcmVzdWx0cyBmcm9tIGEgcm91dGUgY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwMjIzNTggJiBSb3dOdW1fT0cgPD0gMzAyMjM3OCkgfCAjIDMwMjIzNjggIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjk5MzAxNiAmIFJvd051bV9PRyA8PSAyOTkzMDM2KSB8ICMgMjk5MzAyNiAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNjgzNzAzICYgUm93TnVtX09HIDw9IDI2ODM3MjMpICMgMjY4MzcxMyAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkxldCdzIGxvb2sgYXQgdGhlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyBhbmQgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMikuCgpUaGUgOTl0aCBwZXJjZW50aWxlIG9mIFRyYXZlbFRpbWVfU2VjIGZvciBib3RoLCBhbGwgdHJpcHMsIGFuZCBqdXN0IHRob3NlIHRyaXBzIE5PVCBpbnZvbHZpbmcgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMiA9ICJTYW1lIiksIGlzIGFwcHJveGltYXRlbHkgNW1pbiAoMzAwIHNlYykuCgpOb3RhIEJlbmU6ICBUaGUgcGVyY2VudGlsZSBjYWxjdWxhdGlvbiBoZXJlIGlzIGRlZmluZWQgc2xpZ2h0bHkgZGlmZmVyZW50IHRoYW4gaW4gbW9zdCBvZiB0aGUgYWJvdmUgYW5hbHlzZXMgKHdoaWNoIGdldCB0aGUgbG93ZXN0IHZhbHVlIGluIHRoZSBiaW4gY3JlYXRlZCBieSAxMDAgbnRpbGVzKS4KYGBge3J9CgpzdW1tYXJ5KHNlbGVjdChOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIlNhbWUiCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmIDwtIGRhdGEuZnJhbWUoUGN0VmFsdWUgPSBzZXEoMCwgMTAwLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWxsID0gc2VxKDEsIDEwMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbWUgPSBzZXEoMSwgMTAxLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2hhbmdlID0gc2VxKDEsIDEwMSwgMSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpUcmF2VGltZVNlY19RdGlsZXNfZGZbICwgMl0gPC0gcXVhbnRpbGUoc2VsZWN0KE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClRyYXZUaW1lU2VjX1F0aWxlc19kZlsgLCAzXSA8LSBxdWFudGlsZShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmWyAsIDRdIDwtIHF1YW50aWxlKHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyID09ICJDaGFuZ2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClZpZXcoVHJhdlRpbWVTZWNfUXRpbGVzX2RmKQoKYGBgCgoKTGltaXQgdGhlIGRhdGFzZXQgbm93IGJhc2VkIG9uIFRyYXZlbFRpbWVfU2VjLgpgYGB7cn0KClVwcGVyTGltaXRUcmF2VGltZSA8LSBmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjIDw9IDQ5MSAjIG1pbiBhdCB0aGUgMTAwdGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCm5yb3coTm9PdXRsaWVyc1NwZWVkKSAtIG5yb3coVXBwZXJMaW1pdFRyYXZUaW1lKQoKc3RyKFVwcGVyTGltaXRUcmF2VGltZSkKCnN1bW1hcnkoVXBwZXJMaW1pdFRyYXZUaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKRGlmZmVyZW5jZXMgYmV0d2VlbiBEd2VsbF9UaW1lIChieSBXTUFUQSkgYW5kIER3ZWxsX1RpbWUyIChieSBtZSkgYXBwZWFyIHRvIGJlIGR1ZSB0byBzd2l0Y2hlcyBpbiBSb3V0ZUFsdC4gV01BVEEgY2FsY3VsYXRlcyBEd2VsbF9UaW1lIGJ5IGFuIHVua25vd24gcHJvY2Vzcy4gVGhlIFdNQVRBIGNhbGN1bGF0aW9uIGlzIGVxdWFsIHRvIG15IGNhbGN1bGF0aW9uLCBleGNlcHQgZm9yIHRoZSByZWNvcmRzIGltbWVkYWl0ZWx5IGJlZm9yZSBhbmQgYWZ0ZXIgYSBSb3V0ZUFsdCBzd2l0Y2ggKERpckNoYW5nZTIpLgpgYGB7cn0KClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIER3ZWxsX1RpbWUgIT0gRHdlbGxfVGltZTIKICAgICAgICAgICApCiAgICApCgoKIyBFeGFtcGxlcyB3aGVyZSB0aGUgRHdlbGxfVGltZSBhbmQgRHdlbGxfVGltZTIgYXJlIGRpZmZlcmVudApWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAoIChSb3dOdW1fT0cgPj0gNjUgJiBSb3dOdW1fT0cgPD0gODUpIHwgIyA3NQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjIgJiBSb3dOdW1fT0cgPD0gMTkyKSB8ICMgMTcyCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDQzMTk1MiAmIFJvd051bV9PRyA8PSA0MzE5NzIpIHwgIyA0MzE5NjIKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNDM0NTk1ICYgUm93TnVtX09HIDw9IDQzNDYxNSkgIyA0MzQ2MDUgIC0tICB0aGlzIHJlY29yZCBpcyBOT1QgYSByb3V0ZSBzd2l0Y2gsIGJ1dCBkb2VzIGhhcyBhIFNlcXVlbmNlIHN3aXRjaCAoTWU6IHNob3VsZCB0aGVyZSByZWFsbHkgYmUgYSByb3V0ZSBzd2l0Y2ggaGVyZT8pCiAgICAgICAgICAgICkKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpGaXJzdCwgY3JlYXRlIHNvbWUgInJhbmsiIHN0YXRzLgpWaWV3KERUMl9QY3RpbGVzKTogOTUlIG9mIER3ZWxsX1RpbWUycyBhcmUgPD0gMjMgc2Vjb25kcy4uLmJ1dCBzb21lIHdlaXJkIChlLmcuLCBuZWFybHkgMiBob3VyIER3ZWxsX1RpbWUycyBleGlzdCkuCmBgYHtyfQoKRHdlbGxUaW1lMl9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRHdlbGxUaW1lMl9OdGlsZSlbMV0gPC0gIkR3ZWxsX1RpbWUyIgpzdHIoRHdlbGxUaW1lMl9OdGlsZSkKCkR3ZWxsVGltZTJfTnRpbGVfUm93cyA8LSBucm93KER3ZWxsVGltZTJfTnRpbGUpCgpWaWV3KHRhaWwoRHdlbGxUaW1lMl9OdGlsZSwgNTAwKSkKCgpEd2VsbFRpbWUyX1BjdGlsZXMgPC0gZ3JvdXBfYnkoRHdlbGxUaW1lMl9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluRHdlbGxBdFBjdGlsZSA9IG1pbihEd2VsbF9UaW1lMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBEd2VsbFRpbWUyX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEd2VsbFRpbWUyX1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpIaXN0b2dyYW0gb2YgRHdlbGxfVGltZTIuCmBgYHtyfQoKRHdlbGxUaW1lMl9IaXN0RGVuIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyLCBhZXMoeCA9IER3ZWxsX1RpbWUyLCB5ID0gLi5kZW5zaXR5Li4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEsIDI1KSwgeWxpbSA9IGMoMCwgMC4wNSkKICAgICAgICAgICAgICAgICApICsKICB4bGFiKCJUaW1lIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCAoc2VjKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBMb25nIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICxhdG9wKGl0YWxpYygieHh4eHgiKSwiIikKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkR3ZWxsVGltZTJfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKTG9va2luZyBhdCBzb21lIHdlaXJkbHkgbG9uZyBEd2VsbF9UaW1lMiB2YWx1ZXMuCmBgYHtyfQoKVmlldyhhcnJhbmdlKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICBkZXNjKER3ZWxsX1RpbWUyKQogICAgICAgICAgICApCiAgICApCgoKIyBleGFtcGxlcyBvZiBleHRyZW1lbHkgbGFyZ2UgRHdlbGxfVGltZTJzClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjkyNjY5ICYgUm93TnVtX09HIDw9IDI5MjY4OSkgfCAjIDI5MjY3OQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MzEwNTcgJiBSb3dOdW1fT0cgPD0gNTMxMDc3KSB8ICMgNTMxMDY3CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEzODg2MjcgJiBSb3dOdW1fT0cgPD0gMTM4ODY0NykgfCAjIDEzODg2MzcKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTY0NTcxMSAmIFJvd051bV9PRyA8PSAxNjQ1NzMxKSAjIDE2NDU3MjEKICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRHdlbGxfVGltZTIgPT0gMAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpWaWV3KERUMl9QY3RpbGVzKTogOTQlIG9mIERlbHRhX1RpbWUgdmFsdWVzIGFyZSBiZXR3ZWVuIC0yMzYgc2Vjb25kcyBhbmQgMSwyNTkgc2Vjb25kcy4gUm91Z2hseSA2NiUgb2YgcmVjb3JkcyBhcmUgd2l0aGluIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Li4uYnV0IHNvbWUgd2VpcmQgKGUuZy4sIGFsbW9zdCA1MCBtaW51dGUgbGF0ZSBvciA0MCBtaW51dGUgZWFybHkpIERlbHRhX1RpbWVzIGV4aXN0LgoKTm90ZSB0aGF0IERlbHRhX1RpbWUgaXMgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgc2NoZWR1bGVkIGJ1cyBhcnJpdmFsLiBTbyBpZiB0d28gYnVzZXMgYXJlIHNjaGVkdWxlZCB0byBhcnJpdmUgYXQgYSBkZXN0aW5hdGlvbiBhdCAxMDowMHBtIGFuZCAxMDoyMHBtLCBhbmQgaWYgdGhlIDEwOjIwcG0gYnVzIGhhcyBhIERlbHRhX1RpbWUgb2YgNSBtaW51dGVzLCB0aGVyZSBhcmUgMjUgbWludXRlcyBiZXR3ZWVuIGJ1cyBhcnJpdmFscyBhdCB0aGUgc3RvcC4KCkFsc28gbm90ZSB0aGF0IGJhc2VkIG9uIGEgY29tbWVudCBhdCBodHRwczovL3BsYW5pdG1ldHJvLmNvbS8yMDE2LzExLzE2L2RhdGEtZG93bmxvYWQtbWV0cm9idXMtdmVoaWNsZS1sb2NhdGlvbi1kYXRhLywgdGhlIERlbHRhX1RpbWUgdmFsdWVzIGRvbid0IGFwcGVhciB0byBjb2luY2lkZSB3aXRoIHB1Ymxpc2hlZCBidXMgc2NoZWR1bGVzIChlLmcuLCB0aGUgWDIgZGVwYXJ0aW5nIGV2ZXJ5IDggbWludXRlcyBkdXJpbmcgcGVhayBob3VycykuCmBgYHtyfQoKRGVsdFRpbWVfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRGVsdFRpbWVfTnRpbGUpWzFdIDwtICJEZWx0YV9UaW1lIgpzdHIoRGVsdFRpbWVfTnRpbGUpCgpEZWx0VGltZV9OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZV9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWUpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gRGVsdFRpbWVfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpCkRlbHRUaW1lX1BjdGlsZXMKCiMgfjY2JSBvZiByb3dzIGFyZSBiZXR3ZWVuIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Cm5yb3coZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIERlbHRhX1RpbWUgPj0gLTMwMCAmCiAgICAgICAgICAgICAgRGVsdGFfVGltZSA8PSAzMDAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhBbGxEYXlzX05ld09yZGVyKQoKCiMgZXhhbXBsZXMgb2Ygd2VpcmQgbGFyZ2UgRGVsdGFfVGltZXMKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRGVsdGFfVGltZSA8IC00MjAyIHwKICAgICAgICAgICAgICBEZWx0YV9UaW1lID4gMTcwNQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkRlbHRhX1RpbWUgaGlzdG9ncmFtLgpgYGB7cn0KCkRlbHRUaW1lX0hpc3REZW4gPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXIsIGFlcyh4ID0gKERlbHRhX1RpbWUgLyA2MCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gKDUvNjApLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC01LCA1KSkgKwogIHhsYWIoIkJ1cyBMYXRlbmVzcyAobWluKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBFYXJseS9MYXRlIGEgQnVzIElzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBhdG9wKGl0YWxpYygiKHBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscykiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0hpc3REZW4KCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpEZWx0YV9UaW1lIGJveHBsb3QuCmBgYHtyfQoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihEZWx0YV9UaW1lIC8gNjAsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdCA8LSBnZ3Bsb3QoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVsdGFfVGltZSAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFKSArIAogICMgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0zMDAsIDEyMDApKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC01LCAyMCkpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlcywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB4bGFiKCJIb3VyIEdyb3VwIikgKyAKICB5bGFiKCJCdXMgTGF0ZW5lc3MgKG1pbnV0ZXMpIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsgCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiSG93IEVhcmx5L0xhdGUgaXMgdGhlIEJ1cyAoYnkgSG91ciBHcm91cCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGF0b3AoaXRhbGljKCIocG9zaXRpdmUgdmFsdWVzIGFyZSBsYXRlIGFycml2YWxzKSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkV4cGxvcmluZyAiZXh0cmVtZSIgRGVsdGFfVGltZXMuICBGaXJzdCBsZXQncyBnZXQgc29tZSAicmFuayIgc3RhdHMuCmBgYHtyfQoKVmlldyhEZWx0VGltZV9QY3RpbGVzKQpEZWx0VGltZV9QY3RpbGVzCgoKRGVsdFRpbWVBYnNfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSkgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhEZWx0VGltZUFic19OdGlsZSlbMV0gPC0gIkRlbHRhX1RpbWVfQWJzIgpzdHIoRGVsdFRpbWVBYnNfTnRpbGUpCgpEZWx0VGltZUFic19OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVBYnNfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVBYnNfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVBYnNfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZUFic19OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWVfQWJzKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIERlbHRUaW1lX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEZWx0VGltZUFic19QY3RpbGVzKQpEZWx0VGltZUFic19QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIERlbHRhX1RpbWUgKGhvdyBlYXJseSBvciBsYXRlIHRoZSBidXMgaXMpLgoKRXhwbG9yaW5nICJleHRyZW1lIiBEZWx0YV9UaW1lcy4gIFRoZW4gbGV0J3MgY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGJ1c2VzIHRoYXQgYXJlIDEwIG1pbnV0ZXMgKG9yIG1vcmUpIGxhdGUvZWFybHkuCmBgYHtyfQoKSHJHcm91cF9EZWx0YVRpbWVfQWxsIDwtIGdyb3VwX2J5KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKEV2ZW50QWxsX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BbGwpClZpZXcoSHJHcm91cF9EZWx0YVRpbWVfQWxsKQoKCkhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4gPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhEZWx0YV9UaW1lKSA+PSA2MDAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShFdmVudF9UaW1lX0hyR3JvdXApICU+JSAKICBzdW1tYXJpc2UoRXZlbnRBYm92ZTEwX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BYm92ZTEwTWluKQpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4pCgoKSHJHcm91cF9EZWx0YVRpbWVDb21wYXJlIDwtIGlubmVyX2pvaW4oSHJHcm91cF9EZWx0YVRpbWVfQWJvdmUxME1pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSHJHcm91cF9EZWx0YVRpbWVfQWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkV2ZW50X1RpbWVfSHJHcm91cCIgPSAiRXZlbnRfVGltZV9Ickdyb3VwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoUGN0RXZlbnRzQWJvdmUxMCA9IEV2ZW50QWJvdmUxMF9DbnQgLyBFdmVudEFsbF9DbnQpCgpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpRdWlja2x5IHBsb3QgdGhlc2UgImV4dHJlbWUiIERlbHRhX1RpbWVzLiAKYGBge3J9CgpEZWx0VGltZV9BYm92ZTEwX0NvbHMgPC0gZ2dwbG90KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdEV2ZW50c0Fib3ZlMTAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdChyb3VuZChQY3RFdmVudHNBYm92ZTEwLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgbnVkZ2VfeSA9IChIckdyb3VwX0RlbHRhVGltZUNvbXBhcmUkUGN0RXZlbnRzQWJvdmUxMCAqIC0wLjEpCiAgICAgICAgICAgKSArCiAgIyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUsIDUpKSArCiAgeGxhYigiSG91ciBHcm91cCIpICsgCiAgeWxhYigiUGVyY2VudCBvZiBBbGwgQnVzIEFycml2YWxzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJXaGVuIGlzIGEgQnVzIDEwKyBNaW51dGVzIExhdGUvRWFybHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoInBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAiIgogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0Fib3ZlMTBfQ29scwoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKQ29ycmVsYXRpb24uCmBgYHtyfQoKRHdlbGxURGVsdGFUX0NvcnIgPC0gYXMubWF0cml4KGNvcih4ID0gQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZSA9ICJwYWlyd2lzZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCkR3ZWxsVERlbHRhVF9Db3JyCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpOZXh0LCBsZXQncyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcuIExldCdzIGRvIHRoaXMgZm9yIHRoZSBmdWxsIGRhdGFzZXQgKEFsbERheXNfTmV3T3JkZXIpLgpgYGB7cn0KCkFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wIDwtIHNhbXBsZV9mcmFjKEFsbERheXNfTmV3T3JkZXIsIDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIkFsbERhdGEiKQoKc3RyKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wKQoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKTGV0J3MgYWxzbyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcsIGJ1dCB3aXRoIGEgZGF0c2V0IHRoYXQgcmVtb3ZlcyBvdXRsaWVycy4KYGBge3J9CgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpClZpZXcoRHdlbGxUaW1lMl9QY3RpbGVzKQoKQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0d2VlbihEZWx0YV9UaW1lLCAtNDAyLCAxNzA1KSAmICMgcmVtb3ZlcyBhYm91dCAyJSBvZiBEZWx0YV9UaW1lIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oRHdlbGxfVGltZTIsIDEsIDYzKSAgIyByZW1vdmVzIGFib3V0IDIlIG9mIER3ZWxsX1RpbWUyIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIk91dGxpZXJzUmVtb3ZlZCIpCgpzdHIoQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCkKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIG5vdCByZW1vdmUgb3V0bGllcnMuCmBgYHtyfQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIgPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKER3ZWxsX1RpbWUyLCBEZWx0YV9UaW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9zaGFwZShzb2xpZCA9IEZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3VyID0gInJlZCIpICsKICAjIHhsYWIoIlRpbWUgYXQgU3RvcCAoc2VjKSIpICsgCiAgIyB5bGFiKCJMYXRlbmVzcyAoc2VjKSIpICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREd2VsbF9UaW1lMgogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICB4ID0gMjIwMCwKICAgICAgICAgICB5ID0gNjAwLAogICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgY29sb3VyID0gInJlZCIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIsCiAgICAgICBzdWJ0aXRsZSA9ICIobm8gb3V0bGllcnMgcmVtb3ZlZCkiLAogICAgICAgeCA9ICJUaW1lIGF0IFN0b3AgKHNlYykiLAogICAgICAgeSA9ICJMYXRlbmVzcyAoc2VjKSIKICAgICAgKQogICMgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIKICAjICAgICAgICAgICAgICAgICAgICAgICAgICxhdG9wKGl0YWxpYygiKG5vIG91dGxpZXJzIHJlbW92ZWQpIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIHJlbW92ZSBvdXRsaWVycy4KYGBge3J9CgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoRHdlbGxfVGltZTIsIERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG91ciA9ICJibHVlIikgKwogICMgeGxhYigiVGltZSBhdCBTdG9wIChzZWMpIikgKyAKICAjIHlsYWIoIkxhdGVuZXNzIChzZWMpIikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSA1MCwKICAgICAgICAgICB5ID0gLTQ3NSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gIigyJSBvZiBvdXRsaWVycyByZW1vdmVkKSIsCiAgICAgICB4ID0gIlRpbWUgYXQgU3RvcCAoc2VjKSIsCiAgICAgICB5ID0gIkxhdGVuZXNzIChzZWMpIgogICAgICApCiAgIyBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgLGF0b3AoaXRhbGljKCIoMiUgb2Ygb3V0bGllcnMgcmVtb3ZlZCkiKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpQbG90dGluZyB0aGUgZGF0YSBmcm9tIGJvdGggZGF0YXNldHMgdG9nZXRoZXIuCmBgYHtyfQoKQ29tYmluZWREYXRhIDwtIHJiaW5kKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcAogICAgICAgICAgICAgICAgICAgICApCgpDb21iaW5lZERhdGEkRGF0YVNldCA8LSBmYWN0b3IoQ29tYmluZWREYXRhJERhdGFTZXQpCgpzdHIoQ29tYmluZWREYXRhKQoKCkR3ZWxsVERlbHRhVF9TY2F0dGVyX0NvbWJpbmVkIDwtIGdncGxvdChDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IER3ZWxsX1RpbWUyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBEZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERhdGFTZXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA1MDApLCB5bGltID0gYygtMTAwMCwgMjAwMCkKICAgICAgICAgICAgICAgICApICsKICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKENvbWJpbmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFTZXQgPT0gIkFsbERhdGEiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAicmVkIgogICAgICAgICAgICAgKSArCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IGZpbHRlcihDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhU2V0ID09ICJPdXRsaWVyc1JlbW92ZWQiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIKICAgICAgICAgICAgICkgKwogICMgZmFjZXRfd3JhcCggfiBEYXRhU2V0LCBuY29sID0gMikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC02MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC04MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHggPSAiVGltZSBhdCBTdG9wIChzZWMpIiwKICAgICAgIHkgPSAiTGF0ZW5lc3MgKHNlYykiCiAgICAgICkKICAjIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoIjIlIG9mIG91dGxpZXJzIHJlbW92ZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgICAgICkKICAgICAgICAgIyAgICAgICAgICAgICAgICAgKQogICAgICAgICAjICAgICAgICAgICAgKQogICAgICAgICAjICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Db21iaW5lZAoKYGBgCgoKCgoKCgoKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgo=